framework/src/main/java/com/yuanchu/limslaboratory/config/WebMvcConfig.java
@@ -1,23 +1,27 @@ package com.yuanchu.limslaboratory.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; import java.io.File; import java.util.List; @Configuration public class WebMvcConfig extends WebMvcConfigurationSupport { @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { // swaggerå¯è§åé ç½® registry.addResourceHandler("/systemPictures/**") .addResourceLocations("file:" + System.getProperty("user.dir")+ File.separator+"uploadFile"+File.separator+"systemPictures"+File.separator); registry.addResourceHandler("/uploadFile/pluginFiles/logo/**") .addResourceLocations("file:" + System.getProperty("user.dir")+ File.separator+"uploadFile"+File.separator+"pluginFiles"+File.separator+"logo"+File.separator); //é ç½®æ¦æªå¨è®¿é®éæèµæº registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/favicon.ico").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); //设置æä»¶èæè·¯å¾æ å° framework/src/main/java/com/yuanchu/limslaboratory/utils/JwtUtils.java
ÎļþÒÑɾ³ý pom.xml
@@ -32,6 +32,7 @@ <mybatis-plus.version>3.4.0</mybatis-plus.version> <openfeign.version>3.1.3</openfeign.version> <feign-okhttp.version>11.0</feign-okhttp.version> <shiro.version>1.5.3</shiro.version> </properties> <dependencies> @@ -127,6 +128,13 @@ <artifactId>feign-okhttp</artifactId> <version>${feign-okhttp.version}</version> </dependency> <!--å®å ¨æ¡æ¶shiro--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>${shiro.version}</version> </dependency> </dependencies> </dependencyManagement> user-server/pom.xml
@@ -43,6 +43,12 @@ <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency> <!--å®å ¨æ¡æ¶shiro--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> </dependency> </dependencies> </project> user-server/src/main/java/com/yuanchu/limslaboratory/clients/UserLoginUtils.java
@@ -1,5 +1,8 @@ package com.yuanchu.limslaboratory.clients; import com.yuanchu.limslaboratory.pojo.User; import com.yuanchu.limslaboratory.shiro.utils.JwtUtils; import com.yuanchu.limslaboratory.utils.RedisUtil; import com.yuanchu.limslaboratory.vo.Result; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -19,6 +22,7 @@ @Value("${login.secret}") private String LoginSecret; // public Result<Map<String, Object>> LoginExamine(User user){ // Map<String, Object> mapData = new HashMap<>(); // mapData.put("LoginUserID", LoginUserID); @@ -30,7 +34,7 @@ // Map data = (Map) result.getData(); // String token = data.get("token").toString(); // data.remove("token"); // user.setMap(data); //// user.setMap(data); // //åå ¥redis,äºä¸ªå°æ¶åå é¤ // RedisUtil.set(token, user, 2); // // å°ç¾åç JWT token è¿åç»å端 @@ -48,26 +52,17 @@ // } // } public Result<Map<String, Object>> LoginExamine(){ Map<String, Object> mapData = new HashMap<>(); mapData.put("LoginUserID", LoginUserID); mapData.put("LoginSecret", LoginSecret); Result<?> code = userClient.BusynessUserLogin(mapData); if (code.getCode() == 200){ Result<?> result = userClient.BusynessUserLoginToken(code.getData().toString()); if (result.getCode() == 200){ Map data = (Map) result.getData(); String token = data.get("token").toString(); data.remove("token"); // å°ç¾åç JWT token è¿åç»å端 HashMap<String, Object> map = new HashMap<>(); map.put("token", token); return Result.success(map); } else { return Result.fail(result.getMessage()); } } else { return Result.fail(code.getMessage()); } public Result<Map<String, Object>> LoginExamine(User user){ String token = JwtUtils.sign(user.getAccount()); //åå ¥redis,äºä¸ªå°æ¶åå é¤ RedisUtil.set(token, user, 2); // å°ç¾åç JWT token è¿åç»å端 HashMap<String, Object> map = new HashMap<>(); String refresh = JwtUtils.sign(user.getAccount()); map.put("token", token); map.put("refresh", refresh); RedisUtil.set(user.getAccount(), map, 120); return Result.success(map); } } user-server/src/main/java/com/yuanchu/limslaboratory/controller/UserController.java
@@ -1,6 +1,20 @@ package com.yuanchu.limslaboratory.controller; import com.yuanchu.limslaboratory.clients.UserLoginUtils; import com.yuanchu.limslaboratory.pojo.User; import com.yuanchu.limslaboratory.shiro.realm.ShiroRealm; import com.yuanchu.limslaboratory.utils.SpringUtils; import com.yuanchu.limslaboratory.vo.Result; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -15,6 +29,38 @@ */ @RestController @RequestMapping("/user") @Api(tags = "ç¨æ·æ¨¡å") public class UserController { @ApiOperation("ç¨æ·ç»å½") @ApiImplicitParams(value = { @ApiImplicitParam(name = "account", value = "è´¦å·", dataTypeClass = String.class, required = true), @ApiImplicitParam(name = "password", value = "å¯ç ", dataTypeClass = String.class, required = true) }) @PostMapping("/login") public Result<?> UserLogin(String account, String password){ boolean loginSuccess = false; Subject subject = SecurityUtils.getSubject(); if (!subject.isAuthenticated()) { UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(account, password); try { subject.login(usernamePasswordToken); loginSuccess = true; } catch (UnknownAccountException e) { return Result.fail(202, "æ²¡ææ¾å°è¯¥è´¦å·ï¼è¯·æ£æ¥è¾å ¥ï¼"); } catch (IncorrectCredentialsException e) { return Result.fail(202, "å¯ç ä¸å¹é ï¼è¯·æ£æ¥è¾å ¥ï¼"); } } if (loginSuccess) { // è·åshiroRealmä¸çæ°æ® ShiroRealm bean = SpringUtils.getBean(ShiroRealm.class); User user = bean.user; user.setPassword(null); UserLoginUtils bean1 = SpringUtils.getBean(UserLoginUtils.class); return bean1.LoginExamine(user); }else { return Result.fail("ç»å½å¤±è´¥"); } } } user-server/src/main/java/com/yuanchu/limslaboratory/pojo/Enterprise.java
@@ -1,9 +1,14 @@ package com.yuanchu.limslaboratory.pojo; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import java.time.LocalDateTime; import java.io.Serializable; import java.util.Date; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -11,7 +16,7 @@ /** * <p> * * * </p> * * @author æ±èéµ·éç½ç»ç§ææéå ¬å¸ @@ -46,11 +51,13 @@ @ApiModelProperty(value = "è系人çµè¯") private String linkPhone; @ApiModelProperty(value = "å建æ¶é´") private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT) @JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone="GMT+8") private Date createTime; @ApiModelProperty(value = "æ´æ°æ¶é´") private LocalDateTime updateTime; @TableField(fill = FieldFill.INSERT_UPDATE) @JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone="GMT+8") private Date updateTime; @ApiModelProperty(value = "é") private Integer version; user-server/src/main/java/com/yuanchu/limslaboratory/pojo/EnterpriseUserList.java
@@ -1,9 +1,14 @@ package com.yuanchu.limslaboratory.pojo; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import java.time.LocalDateTime; import java.io.Serializable; import java.util.Date; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -11,7 +16,7 @@ /** * <p> * * * </p> * * @author æ±èéµ·éç½ç»ç§ææéå ¬å¸ @@ -37,9 +42,13 @@ @ApiModelProperty(value = "0:å é¤;1:æ£å¸¸") private Integer state; private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT) @JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone="GMT+8") private Date createTime; private LocalDateTime updateTime; @TableField(fill = FieldFill.INSERT_UPDATE) @JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone="GMT+8") private Date updateTime; private Integer version; user-server/src/main/java/com/yuanchu/limslaboratory/pojo/Role.java
@@ -1,9 +1,14 @@ package com.yuanchu.limslaboratory.pojo; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import java.time.LocalDateTime; import java.io.Serializable; import java.util.Date; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -11,7 +16,7 @@ /** * <p> * * * </p> * * @author æ±èéµ·éç½ç»ç§ææéå ¬å¸ @@ -37,9 +42,13 @@ @ApiModelProperty(value = "é»è¾å é¤ æ£å¸¸>=1,å é¤<=0") private Integer state; private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT) @JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone="GMT+8") private Date createTime; private LocalDateTime updateTime; @TableField(fill = FieldFill.INSERT_UPDATE) @JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone="GMT+8") private Date updateTime; @ApiModelProperty(value = "ä¹è§é") private Integer version; user-server/src/main/java/com/yuanchu/limslaboratory/pojo/User.java
@@ -1,17 +1,25 @@ package com.yuanchu.limslaboratory.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import java.time.LocalDateTime; import com.baomidou.mybatisplus.annotation.*; import java.io.Serializable; import java.time.LocalDateTime; import java.util.Date; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import net.sf.jsqlparser.expression.DateTimeLiteralExpression; /** * <p> * * * </p> * * @author æ±èéµ·éç½ç»ç§ææéå ¬å¸ @@ -46,12 +54,20 @@ @ApiModelProperty(value = "å¹´é¾") private Integer age; @TableLogic(value = "1", delval = "0") @ApiModelProperty(value = "å¨èç¶æ æ£å¸¸>=1,离è<=0") private Integer jobState; private LocalDateTime createTime; @ApiModelProperty(value = "个æ§ç¾å") private String info; private LocalDateTime updateTime; @TableField(fill = FieldFill.INSERT) @JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone="GMT+8") private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) @JsonFormat(pattern = "yyyy-MM-dd HH:mm", timezone="GMT+8") private Date updateTime; private Integer version; user-server/src/main/java/com/yuanchu/limslaboratory/service/UserService.java
@@ -3,6 +3,8 @@ import com.yuanchu.limslaboratory.pojo.User; import com.baomidou.mybatisplus.extension.service.IService; import java.io.Serializable; /** * <p> * æå¡ç±» @@ -13,4 +15,5 @@ */ public interface UserService extends IService<User> { User AccordingUsernameSelectAll(String account); } user-server/src/main/java/com/yuanchu/limslaboratory/service/impl/UserServiceImpl.java
@@ -1,10 +1,13 @@ package com.yuanchu.limslaboratory.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.yuanchu.limslaboratory.pojo.User; import com.yuanchu.limslaboratory.mapper.UserMapper; import com.yuanchu.limslaboratory.service.UserService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * <p> @@ -17,4 +20,13 @@ @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { @Resource private UserMapper mapper; @Override public User AccordingUsernameSelectAll(String account) { LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getAccount, account); return mapper.selectOne(wrapper); } } user-server/src/main/java/com/yuanchu/limslaboratory/shiro/JwtToken.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,28 @@ package com.yuanchu.limslaboratory.shiro; import com.yuanchu.limslaboratory.shiro.utils.JwtUtils; import org.apache.shiro.authc.AuthenticationToken; public class JwtToken implements AuthenticationToken { private static final long serialVersionUID = 1L; // å å¯åç JWT token串 private String token; private String account; public JwtToken(String token) { this.token = token; this.account = JwtUtils.getClaimFiled(token, "account"); } @Override public Object getPrincipal() { return this.account; } @Override public Object getCredentials() { return token; } } user-server/src/main/java/com/yuanchu/limslaboratory/shiro/MultiRealmAuthenticator.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,61 @@ package com.yuanchu.limslaboratory.shiro; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.pam.AuthenticationStrategy; import org.apache.shiro.authc.pam.ModularRealmAuthenticator; import org.apache.shiro.realm.Realm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; public class MultiRealmAuthenticator extends ModularRealmAuthenticator { private static final Logger log = LoggerFactory.getLogger(MultiRealmAuthenticator.class); @Override protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) throws AuthenticationException { AuthenticationStrategy strategy = getAuthenticationStrategy(); AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token); if (log.isTraceEnabled()) { log.trace("Iterating through {} realms for PAM authentication", realms.size()); } AuthenticationException authenticationException = null; for (Realm realm : realms) { aggregate = strategy.beforeAttempt(realm, token, aggregate); if (realm.supports(token)) { log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm); AuthenticationInfo info = null; try { info = realm.getAuthenticationInfo(token); } catch (AuthenticationException e) { authenticationException = e; if (log.isDebugEnabled()) { String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:"; log.debug(msg, e); } } aggregate = strategy.afterAttempt(realm, token, info, aggregate, authenticationException); } else { log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token); } } if (authenticationException != null) { throw authenticationException; } aggregate = strategy.afterAllAttempts(token, aggregate); return aggregate; } } user-server/src/main/java/com/yuanchu/limslaboratory/shiro/config/ShiroConfig.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,167 @@ package com.yuanchu.limslaboratory.shiro.config; import com.yuanchu.limslaboratory.shiro.filter.JwtFilter; import com.yuanchu.limslaboratory.shiro.MultiRealmAuthenticator; import com.yuanchu.limslaboratory.shiro.realm.JwtRealm; import com.yuanchu.limslaboratory.shiro.realm.ShiroRealm; import com.yuanchu.limslaboratory.shiro.utils.JwtCredentialsMatcher; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.authc.pam.AuthenticationStrategy; import org.apache.shiro.authc.pam.FirstSuccessfulStrategy; import org.apache.shiro.authc.pam.ModularRealmAuthenticator; import org.apache.shiro.authz.Authorizer; import org.apache.shiro.authz.ModularRealmAuthorizer; import org.apache.shiro.crypto.hash.Md5Hash; import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; import org.apache.shiro.mgt.DefaultSubjectDAO; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.mgt.SessionStorageEvaluator; import org.apache.shiro.realm.Realm; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.Filter; import java.util.*; @Configuration public class ShiroConfig { /** * ä¸å Spring容å¨ä¸æ³¨å JwtFilter Beanï¼é²æ¢ Spring å° JwtFilter 注åä¸ºå ¨å±è¿æ»¤å¨ * å ¨å±è¿æ»¤å¨ä¼å¯¹ææè¯·æ±è¿è¡æ¦æªï¼èæ¬ä¾ä¸åªéè¦æ¦æªé¤ /login å /logout å¤çè¯·æ± * å¦ä¸ç§ç®ååæ³æ¯ï¼ç´æ¥å»æ jwtFilter()ä¸ç @Bean 注解 */ @Bean public FilterRegistrationBean<Filter> registration(JwtFilter filter) { FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<Filter>(filter); registration.setEnabled(false); return registration; } //ShiroFilterè¿æ»¤ææè¯·æ± @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //ç»ShiroFilteré ç½®å®å ¨ç®¡çå¨ shiroFilterFactoryBean.setSecurityManager(securityManager); // æ·»å jwt ä¸ç¨è¿æ»¤å¨ï¼æ¦æªé¤ /login å /logout å¤çè¯·æ± Map<String, Filter> filterMap = new LinkedHashMap<>(); filterMap.put("jwtFilter", new JwtFilter()); shiroFilterFactoryBean.setFilters(filterMap); //é 置系ç»å ¬å ±èµæº Map<String, String> map = new HashMap<String, String>(); // swaggeræ¾è¡ map.put("/doc.html", "anon"); map.put("/webjars/**/**", "anon"); map.put("/swagger-resources", "anon"); map.put("/api-docs", "anon"); map.put("/v3/**", "anon"); map.put("/user/login/**","anon");//表示è¿ä¸ªä¸ºå ¬å ±èµæº ä¸å®æ¯å¨åéèµæºä¸é¢ // map.put("/**","jwtFilter");//表示è¿ä¸ªèµæºéè¦è®¤è¯åææ shiroFilterFactoryBean.setFilterChainDefinitionMap(map); return shiroFilterFactoryBean; } /** * é ç½® ModularRealmAuthenticator */ @Bean public ModularRealmAuthenticator authenticator() { ModularRealmAuthenticator authenticator = new MultiRealmAuthenticator(); // è®¾ç½®å¤ Realmç认è¯çç¥ï¼é»è®¤ AtLeastOneSuccessfulStrategy AuthenticationStrategy strategy = new FirstSuccessfulStrategy(); authenticator.setAuthenticationStrategy(strategy); return authenticator; } /** * ç¦ç¨session, ä¸ä¿åç¨æ·ç»å½ç¶æãä¿è¯æ¯æ¬¡è¯·æ±é½éæ°è®¤è¯ */ @Bean protected SessionStorageEvaluator sessionStorageEvaluator() { DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultSessionStorageEvaluator(); sessionStorageEvaluator.setSessionStorageEnabled(false); return sessionStorageEvaluator; } /** * é ç½® SecurityManagerï¼æé管çå¨ */ @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 1.身份éªè¯å¨ securityManager.setAuthenticator(authenticator()); // 2.管çRealm List<Realm> realms = new ArrayList<Realm>(16); realms.add(JwtRealm()); realms.add(shiroRealm()); securityManager.setRealms(realms); // é ç½®å¤ä¸ªrealm // 3.å ³éshiroèªå¸¦çsession DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator()); securityManager.setSubjectDAO(subjectDAO); return securityManager; } // å建èªå®ä¹Realm @Bean public Realm shiroRealm() { return new ShiroRealm(); } @Bean public Realm JwtRealm(){ return new JwtRealm(); } // 以ä¸ä¸ä¸beanéç¨ï¼åºå®é ç½® /** * äº¤ç± Spring æ¥èªå¨å°ç®¡ç Shiro-Bean ççå½å¨æ */ @Bean public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * 为 Spring-Bean å¼å¯å¯¹ Shiro æ³¨è§£çæ¯æ */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * å¼å¯AOPæ¹æ³çº§æéæ£æ¥ */ @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator; } } user-server/src/main/java/com/yuanchu/limslaboratory/shiro/filter/JwtFilter.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,170 @@ package com.yuanchu.limslaboratory.shiro.filter; import com.yuanchu.limslaboratory.shiro.JwtToken; import com.yuanchu.limslaboratory.shiro.utils.JwtUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter; import org.apache.shiro.web.util.WebUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.RequestMethod; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.PrintWriter; @Component public class JwtFilter extends BasicHttpAuthenticationFilter { private Logger log = LoggerFactory.getLogger(this.getClass()); public Integer code; public String message; /** * åç½®å¤ç */ @Override protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { HttpServletRequest httpServletRequest = WebUtils.toHttp(request); HttpServletResponse httpServletResponse = WebUtils.toHttp(response); // è·¨åæ¶ä¼é¦å åéä¸ä¸ªoption请æ±ï¼è¿éæä»¬ç»option请æ±ç´æ¥è¿åæ£å¸¸ç¶æ if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) { httpServletResponse.setStatus(HttpStatus.OK.value()); return false; } return super.preHandle(request, response); } /** * åç½®å¤ç */ @Override protected void postHandle(ServletRequest request, ServletResponse response) { // æ·»å è·¨åæ¯æ this.fillCorsHeader(WebUtils.toHttp(request), WebUtils.toHttp(response)); } /** * è¿æ»¤å¨æ¦æªè¯·æ±çå ¥å£æ¹æ³ * è¿å true åå è®¸è®¿é® * è¿åfalse åç¦æ¢è®¿é®ï¼ä¼è¿å ¥ onAccessDenied() */ @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { // 妿token为空ä¸è®©éè¿ String header = ((HttpServletRequest) request).getHeader(JwtUtils.AUTH_HEADER); if (ObjectUtils.isEmpty(header)){ code = 503; message = "STS tokenè§£ç è¿ç¨ä¸ï¼ç±äºå é¨è°ç¨å¤±è´¥å¯¼è´è§£ç 失败ï¼è¯·ç¨ååè¯ã"; return false; } boolean allowed = false; try { // æ£æµHeaderéç JWT tokenå 容æ¯å¦æ£ç¡®ï¼å°è¯ä½¿ç¨ tokenè¿è¡ç»å½ allowed = executeLogin(request, response); } catch (IllegalStateException e) { // not found any token log.error("Not found any token"); } catch (Exception e) { log.error("Error occurs when login", e); } return allowed || super.isPermissive(mappedValue); } /** * 身份éªè¯,æ£æ¥ JWT token æ¯å¦åæ³ */ @Override protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception { AuthenticationToken token = createToken(request, response); if (token == null) { String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " + "must be created in order to execute a login attempt."; throw new IllegalStateException(msg); } try { Subject subject = getSubject(request, response); subject.login(token); // äº¤ç» Shiro å»è¿è¡ç»å½éªè¯ return onLoginSuccess(token, subject, request, response); } catch (AuthenticationException e) { return onLoginFailure(token, e, request, response); } } /** * ä» Header éæå JWT token */ @Override protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; String authorization = httpServletRequest.getHeader(JwtUtils.AUTH_HEADER); JwtToken token = new JwtToken(authorization); return token; } /** * isAccessAllowed()æ¹æ³è¿åfalseï¼ä¼è¿å ¥è¯¥æ¹æ³ï¼è¡¨ç¤ºæç»è®¿é® */ @Override protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { HttpServletResponse httpResponse = WebUtils.toHttp(servletResponse); httpResponse.setCharacterEncoding("UTF-8"); httpResponse.setContentType("application/json;charset=UTF-8"); httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value()); PrintWriter writer = httpResponse.getWriter(); writer.write("{\"errCode\": "+ code +", \"msg\": \"" + message +"\"}"); fillCorsHeader(WebUtils.toHttp(servletRequest), httpResponse); return false; } /** * Shiro å©ç¨ JWT token ç»å½æåï¼ä¼è¿å ¥è¯¥æ¹æ³ */ @Override protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) { HttpServletResponse httpResponse = WebUtils.toHttp(response); String newToken = null; if (token instanceof JwtToken) { newToken = JwtUtils.refreshTokenExpired(token.getCredentials().toString(), JwtUtils.SECRET); } if (newToken != null) httpResponse.setHeader(JwtUtils.AUTH_HEADER, newToken); return true; } /** * Shiro å©ç¨ JWT token ç»å½å¤±è´¥ï¼ä¼è¿å ¥è¯¥æ¹æ³ */ @Override protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { // æ¤å¤ç´æ¥è¿å false ï¼äº¤ç»åé¢ç onAccessDenied()æ¹æ³è¿è¡å¤ç String error = e.getClass().toString().split("\\.")[4]; if (error.equals("IncorrectCredentialsException")) { code = 10010; } else { code = 401; } message = e.getMessage(); return false; } /** * æ·»å è·¨åæ¯æ */ protected void fillCorsHeader(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin")); httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,HEAD"); httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers")); } } user-server/src/main/java/com/yuanchu/limslaboratory/shiro/realm/JwtRealm.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,57 @@ package com.yuanchu.limslaboratory.shiro.realm; import com.yuanchu.limslaboratory.pojo.User; import com.yuanchu.limslaboratory.shiro.JwtToken; import com.yuanchu.limslaboratory.shiro.utils.JwtCredentialsMatcher; import com.yuanchu.limslaboratory.utils.RedisUtil; import org.apache.shiro.authc.*; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; public class JwtRealm extends AuthorizingRealm { /** * éå®è¿ä¸ª Realm åªå¤çæä»¬èªå®ä¹ç JwtToken */ @Override public boolean supports(AuthenticationToken token) { return token instanceof JwtToken; } /** * æ¤å¤ç SimpleAuthenticationInfo å¯è¿åä»»æå¼ï¼å¯ç æ ¡éªæ¶ä¸ä¼ç¨å°å® */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { JwtToken jwtToken = (JwtToken) authcToken; if (jwtToken.getPrincipal() == null) { throw new AccountException("Tokenåæ°å¼å¸¸ï¼"); } // å½åç¨æ· String account = jwtToken.getPrincipal().toString(); // å½åç¨æ·çtoken String credentials = (String)jwtToken.getCredentials(); User user = (User) RedisUtil.get(credentials); // ç¨æ·ä¸åå¨ if (user == null) { throw new UnknownAccountException("ç¨æ·ä¸åå¨ï¼"); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, account, getName()); return info; } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); return info; } @Override public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) { // 设置å å¯ç®æ³ CredentialsMatcher jwtCredentialsMatcher = new JwtCredentialsMatcher(); super.setCredentialsMatcher(jwtCredentialsMatcher); } } user-server/src/main/java/com/yuanchu/limslaboratory/shiro/realm/ShiroRealm.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,55 @@ package com.yuanchu.limslaboratory.shiro.realm; import com.yuanchu.limslaboratory.pojo.User; import com.yuanchu.limslaboratory.service.UserService; import com.yuanchu.limslaboratory.utils.MyUtils; import com.yuanchu.limslaboratory.utils.SpringUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.util.ObjectUtils; public class ShiroRealm extends AuthorizingRealm { public User user; /** * éå®è¿ä¸ª Realm åªå¤ç UsernamePasswordToken */ @Override public boolean supports(AuthenticationToken token) { return token instanceof UsernamePasswordToken; } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) { String principal = (String) authenticationToken.getPrincipal(); //è·åUserService对象 UserService userService = SpringUtils.getBean(UserService.class); user = userService.AccordingUsernameSelectAll(principal); MyUtils.PrintLog(user.toString()); if (!ObjectUtils.isEmpty(user)) { return new SimpleAuthenticationInfo(user.getAccount(), user.getPassword(), this.getName()); } else { throw new UnknownAccountException(); } } @Override public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) { // HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); // //设置使ç¨MD5å å¯ç®æ³ // hashedCredentialsMatcher.setHashAlgorithmName(Md5Hash.ALGORITHM_NAME); // //æ£åæ¬¡æ° // hashedCredentialsMatcher.setHashIterations(1024); // super.setCredentialsMatcher(hashedCredentialsMatcher); super.setCredentialsMatcher(credentialsMatcher); } } user-server/src/main/java/com/yuanchu/limslaboratory/shiro/utils/JwtCredentialsMatcher.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,34 @@ package com.yuanchu.limslaboratory.shiro.utils; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTVerificationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.springframework.stereotype.Component; @Component public class JwtCredentialsMatcher implements CredentialsMatcher { /** * JwtCredentialsMatcheråªééªè¯JwtTokenå 容æ¯å¦åæ³ */ @Override public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) { String token = authenticationToken.getCredentials().toString(); String account = authenticationToken.getPrincipal().toString(); try { Algorithm algorithm = Algorithm.HMAC256(JwtUtils.getSecret()); JWTVerifier verifier = JWT.require(algorithm).withClaim("account", account).build(); verifier.verify(token); return true; } catch (JWTVerificationException e) { } catch (Exception e){ e.printStackTrace(); } return false; } } user-server/src/main/java/com/yuanchu/limslaboratory/shiro/utils/JwtUtils.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,160 @@ package com.yuanchu.limslaboratory.shiro.utils; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTCreator; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTCreationException; import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import javax.annotation.PostConstruct; import java.io.UnsupportedEncodingException; import java.util.Calendar; import java.util.Date; import java.util.Map; @Component public class JwtUtils { private static String staticSecret; @Value("${login.secret}") private String secret; @PostConstruct public void getApiToken() { staticSecret = this.secret; } public static String getSecret() { // lockie.zou return staticSecret; } // è¿ææ¶é´ 2 å°æ¶ private static final long EXPIRE_TIME = 2 * 60 * 60 * 1000; //èªå·±å®å¶å¯é¥ public static final String SECRET = "J-(t]Poe9P"; //请æ±å¤´ public static final String AUTH_HEADER = "X-Token"; // X-Authorization-With /** * éªè¯tokenæ¯å¦æ£ç¡® * @param token * @return */ public static boolean verify(String token){ try{ String account = getClaimFiled(token, "account"); if (account == null){ return false; } Algorithm algorithm = Algorithm.HMAC256(getSecret()); JWTVerifier verifier = JWT.require(algorithm).withClaim("account",account).build(); verifier.verify(token); return true; } catch (JWTVerificationException exception){ return false; } catch (Exception e){ e.printStackTrace(); return false; } } /** * è·å¾tokenä¸çèªå®ä¹ä¿¡æ¯,ä¸è¬æ¯è·åtokençusernameï¼æ ésecretè§£å¯ä¹è½è·å¾ * @param token * @param filed * @return */ public static String getClaimFiled(String token, String filed){ try{ if (!ObjectUtils.isEmpty(token)){ DecodedJWT jwt = JWT.decode(token); return jwt.getClaim(filed).asString(); } return null; } catch (JWTDecodeException e){ return null; } } /** * çæç¾å,åç¡®å°è¯´æ¯çætoken * @return */ public static String sign(String account){ try{ Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); Algorithm algorithm = Algorithm.HMAC256(SECRET); //é带username,nicknameä¿¡æ¯ return JWT.create() .withClaim("account",account) .withExpiresAt(date) .sign(algorithm); } catch (JWTCreationException e){ e.printStackTrace(); return null; } catch (Exception e){ e.printStackTrace(); return null; } } /** * è·åtokençç¾åæ¶é´ * @param token * @return */ public static Date getIssueAt(String token){ try{ DecodedJWT jwt = JWT.decode(token); return jwt.getIssuedAt(); } catch (JWTDecodeException e){ e.printStackTrace(); return null; } } /** * éªè¯tokenæ¯å¦è¿æ * @param token * @return */ public static boolean isTokenExpired(String token){ Date now = Calendar.getInstance().getTime(); DecodedJWT jwt = JWT.decode(token); return jwt.getExpiresAt().before(now); } /** * å·æ°tokençæææ * @param token * @param secret * @return */ public static String refreshTokenExpired(String token, String secret){ DecodedJWT jwt = JWT.decode(token); //è§£ætoken Map<String, Claim> claims = jwt.getClaims(); //è·åtokençåæ°ä¿¡æ¯ try{ Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); Algorithm algorithm = Algorithm.HMAC256(secret); JWTCreator.Builder builder = JWT.create().withExpiresAt(date); for(Map.Entry<String,Claim> entry : claims.entrySet()){ builder.withClaim(entry.getKey(),entry.getValue().asString()); } return builder.sign(algorithm); } catch (JWTCreationException e){ e.printStackTrace(); return null; } } } user-server/src/main/resources/mapper/AdminMapper.xml
ÎļþÒÑɾ³ý