springboot+shiro之应用
发布日期:2021-05-27 02:54:25 浏览次数:4 分类:技术文章

本文共 16592 字,大约阅读时间需要 55 分钟。

在web项目日常开发中,轻量级安全框架shiro主要用来进行认证和授权,可以应用在需要对用户登录、角色、url进行权限控制,也可用应用过滤器进行其他校验。权限控制主要通过用户、角色、权限、用户角色关系、角色权限关系进行维护。

  1. pom文件
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.4.3
com
springboot
0.0.1-SNAPSHOT
springboot
Demo project for Spring Boot
1.8
net.sf.json-lib
json-lib
2.4
jdk15
org.crazycake
shiro-redis
3.2.3
org.apache.shiro
shiro-spring
1.4.0
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
2.4.3
  1. application.properties
spring.redis.database=0spring.redis.host=127.0.0.1spring.redis.port=6379spring.redis.password=
  1. ShiroSessionManager
public class ShiroSessionManager extends DefaultWebSessionManager {    /**     * 重写构造器     */    public ShiroSessionManager() {        super();        this.setDeleteInvalidSessions(true);    }    /**     * 重写方法实现从请求头获取Token便于接口统一     * 每次请求进来,Shiro会去从请求头找REQUEST_HEADER这个key对应的Value(Token)     */    @Override    public Serializable getSessionId(ServletRequest request, ServletResponse response) {        String token = WebUtils.toHttp(request).getHeader("Authorization");        // 如果请求头中存在token 则从请求头中获取token        if (!ObjectUtils.isEmpty(token)) {            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "Stateless request");            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, token);            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);            return token;        } else {            // 否则按默认规则从cookie取token            return super.getSessionId(request, response);        }    }}
  1. MyShiroRealm
public class MyShiroRealm extends AuthorizingRealm {    @Autowired    private UserService userService;    @Autowired    private RoleService roleService;    @Autowired    private PermissionService permissionService;        @Override    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();        // 获取当前登录用户信息        SysUser currentUser = (SysUser) principalCollection.getPrimaryPrincipal();        // 根据用户名称去数据库查询用户、角色、权限信息        List
roleList = roleService.getRoleListByUserId(currentUser.getId()); for (SysRole role : roleList) { // 添加角色 simpleAuthorizationInfo.addRole(role.getName()); } List
permissionList = permissionService.getPermissionListByUserId(currentUser.getId()); for (SysPermission permission : permissionList) { // 添加权限 simpleAuthorizationInfo.addStringPermission(permission.getPermission()); } return simpleAuthorizationInfo; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { // 获取当前登录用户信息 String name = authenticationToken.getPrincipal().toString(); // 根据用户名称去数据库查询用户 SysUser user = userService.getUserByName(name); // 根据userId获取role信息 if (user == null) { //这里返回后会报出对应异常 return null; } else { // 这里验证authenticationToken和simpleAuthenticationInfo的信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo( user, user.getPwd(), // 报序列化错误 // ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt ByteSourceUtils.bytes(user.getCredentialsSalt()),//salt=username+salt getName() ); return simpleAuthenticationInfo; } } public void clearAllCachedAuthorizationInfo() { getAuthorizationCache().clear(); } public void clearAllCachedAuthenticationInfo() { getAuthenticationCache().clear(); } public void clearAllCache() { clearAllCachedAuthenticationInfo(); clearAllCachedAuthorizationInfo(); }}
  1. ShiroConfig
@Configurationpublic class ShiroConfig {    private final String CACHE_KEY = "shiro:cache:";    private final String SESSION_KEY = "shiro:session:";    // cacheManager、redisSessionDAO 过期时间    private final int EXPIRE = 1800;    @Autowired    private RedisProperties redisProperties;    @Autowired    private PermissionService permissionService;    @Bean    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);        return authorizationAttributeSourceAdvisor;    }    @Bean    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();        shiroFilterFactoryBean.setSecurityManager(securityManager);        // 自定义过滤器        Map
filters = new HashMap<>(); // 用户登录状态校验 filters.put("user", new SecurityFormAuthenticationFilter()); // 角色权限校验 filters.put("croles", new SecurityRoleAuthorizationFilter()); // 接口权限校验 filters.put("cperms", new SecurityPermissionAuthorizationFilter()); // 用户退出系统 filters.put("logout", new SecurityLogoutFilter()); shiroFilterFactoryBean.setFilters(filters); // 自定义过滤器链 Map
filterChainDefinitionMap = new LinkedHashMap
(); // 顺序判断;authc:所有url都必须认证通过才可以访问;anon:所有url都都可以匿名访问 filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/getVerifyCode", "anon"); filterChainDefinitionMap.put("/getSid", "anon"); filterChainDefinitionMap.put("/index.html*", "anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/logout", "logout"); List
permissionInfoList = permissionService.getAllRolePermissionList(); // 数据库读取所有角色、权限信息,拦截校验 for (PermissionInfo permissionInfo : permissionInfoList) { List
roleList = permissionInfo.getRoleList(); String roleSet = ""; for (SysRole role : roleList) { roleSet += role.getName() + ","; } filterChainDefinitionMap.put(permissionInfo.getUrl().startsWith("/") ? permissionInfo.getUrl() : "/" + permissionInfo.getUrl(), "user,authc" + ",cperms[" + permissionInfo.getPermission() + "]" + ("".equals(roleSet) ? "" : ",croles[" + roleSet.substring(0, roleSet.length() - 1) + "]")); System.out.println("shiro config: url" + (permissionInfo.getUrl().startsWith("/") ? permissionInfo.getUrl() : "/" + permissionInfo.getUrl()) + "===" + ("user,authc" + ",cperms[" + permissionInfo.getPermission() + "]" + ("".equals(roleSet) ? "" : ",croles[" + roleSet.substring(0, roleSet.length() - 1) + "]"))); } filterChainDefinitionMap.put("/user/hello", "user,authc,croles[super]"); filterChainDefinitionMap.put("/**", "user,authc"); // 登录 //shiroFilterFactoryBean.setLoginUrl("/login"); // 首页 //shiroFilterFactoryBean.setSuccessUrl("/index"); // 错误页面,认证不通过跳转 //shiroFilterFactoryBean.setUnauthorizedUrl("/error"); // 将filterChainDefinitionMap放入过滤器链 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 自定义session管理 securityManager.setSessionManager(sessionManager()); // 自定义Cache实现缓存管理 securityManager.setCacheManager(cacheManager()); // 自定义Realm验证 securityManager.setRealm(myShiroRealm()); return securityManager; } @Bean public MyShiroRealm myShiroRealm() { MyShiroRealm myShiroRealm = new MyShiroRealm(); myShiroRealm.setCachingEnabled(true); myShiroRealm.setAuthenticationCachingEnabled(true); myShiroRealm.setAuthorizationCachingEnabled(true); myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return myShiroRealm; } @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher shaCredentialsMatcher = new HashedCredentialsMatcher(); // 散列算法:这里使用SHA256算法; shaCredentialsMatcher.setHashAlgorithmName("md5"); // 散列的次数,比如散列两次,相当于 md5(md5("")); shaCredentialsMatcher.setHashIterations(2); return shaCredentialsMatcher; } @Bean public RedisCacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); redisCacheManager.setKeyPrefix(CACHE_KEY); // 配置缓存的话要求放在session里面的实体类必须有个id标识 redisCacheManager.setPrincipalIdFieldName("id"); // 用户权限信息缓存时间 单位秒 此为30分钟 redisCacheManager.setExpire(EXPIRE); return redisCacheManager; } @Bean public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setDatabase(redisProperties.getDatabase()); redisManager.setHost(redisProperties.getHost() + ":" + redisProperties.getPort()); // redisManager.setPort(port); // 若在此设置密码则不能取空密码,需设置redis密码 // redisManager.setPassword(redisProperties.getPassword()); return redisManager; } @Bean public SessionManager sessionManager() { ShiroSessionManager shiroSessionManager = new ShiroSessionManager(); shiroSessionManager.setSessionDAO(redisSessionDAO()); return shiroSessionManager; } @Bean public RedisSessionDAO redisSessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); redisSessionDAO.setSessionIdGenerator(sessionIdGenerator()); // redisSessionDAO.setValueSerializer(new ByteSourceSerializer()); redisSessionDAO.setKeyPrefix(SESSION_KEY); // 此为30分钟 redisSessionDAO.setExpire(EXPIRE); return redisSessionDAO; } @Bean public ShiroSessionIdGenerator sessionIdGenerator() { return new ShiroSessionIdGenerator(); }}
  1. SecurityLogoutFilter
public class SecurityLogoutFilter extends LogoutFilter {    private static final Logger log = LoggerFactory.getLogger(SecurityLogoutFilter.class);    @Value("${safe.switch}")    private String safe;    @Override    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {        HttpServletRequest req = (HttpServletRequest) request;        HttpServletResponse resp = (HttpServletResponse) response;        Subject subject = getSubject(request, response);        SysUser currentUser = (SysUser) subject.getPrincipal();        resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));        resp.setHeader("Access-Control-Allow-Credentials", "true");        resp.setContentType("application/json; charset=utf-8");        resp.setCharacterEncoding("UTF-8");        String ip = IPUtil.getIpAddress((HttpServletRequest) request);        String url = ((HttpServletRequest) request).getRequestURL() + "";        if (currentUser == null) {            log.info("用户未登录,无法退出系统");            ResponseUtil.returnResultAjax(resp, JSONUtil.object2JSONStr(ResponseData.fail("用户未登录,无法退出系统",400)), null);        } else {            logout(subject);            log.info("用户" + currentUser.getName() + "正常退出!");            if("on".equals(safe)) {                ResponseUtil.returnResultAjax(resp, "退出登录成功", null);            }else {                ResponseUtil.returnResultAjax(resp, "退出登录成功", null);            }        }        return false;    }    public static void logout(Subject subject) {        if (subject == null) {            return;        }        subject.logout();    }}
  1. login
@RequestMapping(value = "/login", method = RequestMethod.POST)    public Object login(HttpServletRequest request, HttpServletResponse response, @RequestBody Map
params) { String username = params.get("username"); String pwd = params.get("pwd"); String plainPwd = ""; if ("on".equals(safe)) { //decrypt plainPwd = pwd; } else { plainPwd = pwd; } try { Subject subject = SecurityUtils.getSubject(); System.out.println("登录前sessionId:"+subject.getSession().getId()); // 让旧session失效 subject.logout(); UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, plainPwd); // 在此清除认证和授权缓存,需去数据库重新获取用户信息 // myShiroRealm.clearAllCache(); subject.login(usernamePasswordToken); System.out.println("登陆成功后sessionId:" + subject.getSession().getId()); SysUser user = (SysUser) SecurityUtils.getSubject().getPrincipal(); Map
returnData = new HashMap<>(); if (user != null) { returnData.put("userId", String.valueOf(user.getId())); returnData.put("userName", user.getUserName()); returnData.put("token", subject.getSession().getId().toString()); // sessionId与登录用户的对应关系 subject.getSession().setAttribute(subject.getSession().getId().toString(), returnData); // 记录登录用户名与IP之间的关系 subject.getSession().setAttribute(user.getName(), IPUtil.getIpAddress(request)); // 为了让请求头中的Cookie失效 Cookie[] cookies= request.getCookies(); if(cookies!=null){ for (Cookie cookie :cookies){ cookie.setPath("/"); cookie.setHttpOnly(true); cookie.setMaxAge(0); cookie.setValue(""); response.addCookie(cookie); } } } return ResponseData.ok(returnData, "登录成功"); } catch (AuthenticationException ce) { if (ce instanceof UnknownAccountException) { return ResponseData.fail("用户不存在", 1001); } else if (ce instanceof IncorrectCredentialsException) { return ResponseData.fail("密码错误", 1002); } else if (ce instanceof LockedAccountException) { return ResponseData.fail("用户被锁定", 1003); } else if (ce instanceof ExcessiveAttemptsException) { return ResponseData.fail("非法尝试", 1004); } else { return ResponseData.fail("登录出现异常", 999); } } }

访问测试结果

{    "code": 10019,    "data": null,    "message": "会话过期或未登陆",    "status": false}
{    "status": false,    "code": 20001,    "message": "验证码不正确或已过期,请重新输入或者刷新验证码",    "data": null}
{    "code": 19909,    "data": null,    "message": "url越权访问:http://***",    "status": false}
{    "code": 400,    "data": null,    "message": "用户未登录,无法退出系统",    "status": false}
{    "status": true,    "code": 200,    "message": null,    "data": "getUser1"}

 

转载地址:https://blog.csdn.net/leijie0322/article/details/116571647 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Java之死循环中new对象出现OOM问题
下一篇:Java之proto初尝试

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2023年11月07日 09时36分46秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章