XiaoRuby
2023-07-07 efbed347c8b659ca13207a1dbe34e77fdb003949
LIMS管理系统框架-开发-1.0.0
已修改11个文件
已删除2个文件
已添加8个文件
981 ■■■■■ 文件已修改
framework/src/main/java/com/yuanchu/limslaboratory/config/WebMvcConfig.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/com/yuanchu/limslaboratory/utils/JwtUtils.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/pom.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/clients/UserLoginUtils.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/controller/UserController.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/pojo/Enterprise.java 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/pojo/EnterpriseUserList.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/pojo/Role.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/pojo/User.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/service/UserService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/service/impl/UserServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/shiro/JwtToken.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/shiro/MultiRealmAuthenticator.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/shiro/config/ShiroConfig.java 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/shiro/filter/JwtFilter.java 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/shiro/realm/JwtRealm.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/shiro/realm/ShiroRealm.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/shiro/utils/JwtCredentialsMatcher.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/java/com/yuanchu/limslaboratory/shiro/utils/JwtUtils.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
user-server/src/main/resources/mapper/AdminMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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
ÎļþÒÑɾ³ý