zoukankan      html  css  js  c++  java
  • SpringBoot使用SpringSecurity和Shiro和swagger

    SpringBoot文档:http://felord.cn/_doc/_springboot/2.1.5.RELEASE/_book/
    https://docs.spring.io/spring-boot/docs/current/reference/html/
    系统安全框架,用于认证、授权 
    1、SpringSecurity依赖

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>2.5.3</version>
    </dependency>
    

    2、命名空间xmlns:th=http://www.thymeleaf.org   xmlns:sec=http://www.thymeleaf.org/extras/spring-security   xmlns:shiro=http://www.pollix.at/thymeleaf/shiro

    3、自己创建SecurityConfig

    package com.jay.SpringBootStudy8.config;
    
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import static org.springframework.security.config.Customizer.withDefaults;
    
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        //认证
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    //        super.configure(auth);
            //BCryptPasswordEncoder 加密方式,用户名admin、密码123456,可以and()连接多个
            auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                    .withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1")
                    .and()
                    .withUser("user1").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2")
                    ;
        }
        //授权
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    //        http.authorizeRequests(authorize -> authorize.anyRequest().authenticated()).oauth2Login(withDefaults());
            //首页/所有人可访问
            //功能页只有具有权限的人才能访问
            http.authorizeRequests()
                    .antMatchers("/level1").hasRole("vip1")
                    .antMatchers("/level2").hasRole("vip2")
                    .antMatchers("/level3/**").hasRole("vip3");
    
            //禁用csrf跨站
            http.csrf().disable();
            //开启登录,定制登录页,自定义请求参数名 和 登录请求地址。
            //如果不自定义登录请求地址,那登录form的action需要是loginPage指定的地址 /userLogin
            http.formLogin().loginPage("/userLogin")
                    .usernameParameter("uname")
                    .passwordParameter("pwd")
                    .loginProcessingUrl("/loginHandler");
            //记住我,自定义请求参数
            http.rememberMe().rememberMeParameter("remember");
            //注销、删除cookie、清除session
            http.logout().deleteCookies("remove").invalidateHttpSession(true);
        }
    }
    

     表单userLogin:

    <form method="post" th:action="@{/loginHandler}">
    username:<input type="text" name="uname" />
    <br/>
    password:<input type="password" name="pwd" />
    <br/>
    <input type="checkbox" name="remember" />Remember Me
    <br/>
    <button type="submit">submit</button>
    </form>
    

     loginHandler可以处理自己其他的登录逻辑。
    页面中获取Spring Security登录用户数据:https://www.cnblogs.com/softidea/p/6677665.html     https://blog.csdn.net/cyan20115/article/details/106552758
    Spring Security使用jdbc进行权限验证:https://blog.csdn.net/fuzekun/article/details/104344472
    Shiro:https://www.jianshu.com/p/7f724bec3dc3
    1、引入相关包

    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.7.1</version>
    </dependency>
    
    <dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.1.0</version>
    </dependency>
    
    <!--工具类-->
    <dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.8</version>
    </dependency>
    

    2、 自定义ShiroConfig

    package com.jay.SpringBootStudy8.config;
    
    import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
    import org.apache.shiro.mgt.SecurityManager;
    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.beans.factory.annotation.Qualifier;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    @Configuration
    public class ShiroConfig {
        //Filter工厂,设置对应的过滤条件和跳转条件
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
            ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
            bean.setSecurityManager(securityManager);
            /*
            anon:无需认证即可访问
            authc:必须认证才可访问
            user:必须拥有记住我的功能,才可以用
            perms:必须拥有对某个资源的权限才可以访问
            role:拥有某个角色才可以访问
             */
            Map<String, String> filterMap = new LinkedHashMap<>();
            //对所有用户认证,只要登录成功,就可以访问
            //filterMap.put("/sys/*", "authc");
    
            //必须拥有资源权限(推荐),users:mgr 是权限码,存于数据库
    //        filterMap.put("/sys/users", "perms[users:mgr]");
            filterMap.put("/sys/roles", "perms[roles:mgr]");
            filterMap.put("/sys/permissions", "perms[permissions:mgr]");
            filterMap.put("/sys/products", "perms[products:mgr]");
            filterMap.put("/sys/articles", "perms[articles:mgr]");
            filterMap.put("/sys/test", "perms[sys:test]");
    
            filterMap.put("/logout", "logout");//登出
            filterMap.put("/loginHandler", "anon");
            bean.setLoginUrl("/login");//登录
            bean.setSuccessUrl("/sys/main");//首页
            bean.setUnauthorizedUrl("/noauth");//错误页面,认证不通过跳转
            bean.setFilterChainDefinitionMap(filterMap);
            return bean;
        }
    
        //权限管理,配置主要是Realm的管理认证
        @Bean(name = "securityManager")
        public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("customRealm") CustomRealm customRealm) {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(customRealm);
            return securityManager;
        }
    
        //将自己的验证方式加入容器
        @Bean
        public CustomRealm customRealm() {
            return new CustomRealm();
        }
    
        //集成 thymeleaf-extras-shiro,可以在前端使用shiro语法
        @Bean
        public ShiroDialect getShiroDialect(){return  new ShiroDialect();}
    }
    

    shiro异常处理配置:

    package com.jay.SpringBootStudy8.config;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
    import java.util.Properties;
    @Configuration
    public class ShiroExceptionConf {
        @Bean
        public SimpleMappingExceptionResolver resolver() {
            SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
            Properties properties = new Properties();
            properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/noauth");
            resolver.setExceptionMappings(properties);
            return resolver;
        }
    }
    

     3、自定义CustomRealm

    package com.jay.SpringBootStudy8.config;
    
    import com.jay.SpringBootStudy8.pojo.Permission;
    import com.jay.SpringBootStudy8.pojo.Role;
    import com.jay.SpringBootStudy8.pojo.SysUser;
    import com.jay.SpringBootStudy8.service.PermissionService;
    import com.jay.SpringBootStudy8.service.RoleService;
    import com.jay.SpringBootStudy8.service.SysUserService;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.ByteSource;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import java.util.List;
    import java.util.stream.Collectors;
    
    public class CustomRealm extends AuthorizingRealm {
        @Autowired
        private SysUserService sysUserService;
        @Autowired
        private RoleService roleService;
        @Autowired
        private PermissionService permissionService;
    
        //授权,装配用户权限
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    //        Subject subject = SecurityUtils.getSubject();
    //        Session session = subject.getSession();
    //        SysUser user = (SysUser) session.getAttribute("user");
    
            //获取登录用户
            SysUser sysUser = (SysUser) principalCollection.getPrimaryPrincipal();
            List<Permission> permissions = sysUser.getPermissions();
            List<Role> roles = sysUser.getRoles();
            //添加角色和权限
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            for (Role role : roles) {
                //添加角色
                info.addRole(role.getName());
            }
            //添加权限
            for (Permission perm : permissions) {
                info.addStringPermission(perm.getCode());
            }
            return info;
        }
    
        //认证,验证用户的账户和密码
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
            String username = userToken.getUsername();
            SysUser user = sysUserService.getModelByUserName(username);
            if (user == null) {
                return null;//这里返回后会报出对应异常
            } else {
                List<Role> roles = roleService.getAllByUserId(user.getId());
                user.setRoles(roles);
                List<Integer> roleIds = roles.stream().map(Role::getId).collect(Collectors.toList());
                List<Permission> perms = permissionService.getAllByRoleIds(roleIds);
                user.setPermissions(perms);
                //盐值加密验证密码
                String realmName = getName();
                ByteSource credentialsSalt = ByteSource.Util.bytes(username);//这里的参数要给个唯一的;
                SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPwd(), credentialsSalt, realmName);
                // 存入Shiro的Session,这里不是HttpSession,是Shiro独立的Session,
                // 只能通过SecurityUtils.getSubject().getSession()获取
                Subject subject = SecurityUtils.getSubject();
                Session session = subject.getSession();
                session.setAttribute("user", user);
                return info;
            }
        }
    }
    

    4、登录Controller

    package com.jay.SpringBootStudy8.controller;
    
    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.crypto.hash.SimpleHash;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.ByteSource;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    //在templates目录下的页面只能通过controller来跳转
    @Controller
    public class IndexController {
        @RequestMapping("/noauth")
        public String unauthorized() {
            return "unauthorized";
        }
    
        @RequestMapping({"/", "/login"})
        public String login() {
            return "login";
        }
    
        @RequestMapping("/logout")
        public String logout() {
            Subject subject = SecurityUtils.getSubject();
            subject.logout();
            return "login";
        }
    
        @PostMapping("/loginHandler")
        public String loginHandler(String name, String pwd, Model model) {
            //MD5加密
            ByteSource credentialsSalt = ByteSource.Util.bytes(name);
            Object obj = new SimpleHash("MD5", pwd, credentialsSalt, 1);
            String pwd2 = obj.toString();
    
            //获取当前用户
            Subject subject = SecurityUtils.getSubject();
            //封装登录数据
            UsernamePasswordToken token = new UsernamePasswordToken(name, pwd2);
            try {
                //执行登录方法
                subject.login(token);
                return "redirect:/sys/main";
            } catch (UnknownAccountException e) {
                model.addAttribute("msg", "用户名不存在");
                return "login";
            } catch (IncorrectCredentialsException e) {
                model.addAttribute("msg", "密码错误");
                return "login";
            }
        }
    }
    

    5、 数据库中权限表:permissions

    Id     Name        Code
    --------------------------------------
    1	用户管理	users:mgr
    2	角色管理	roles:mgr
    3	权限管理	permissions:mgr
    4	商品管理	products:mgr
    5	图文管理	articles:mgr
    

    6、前端需要整合

    <div th:fragment="header">
        <a th:href="@{/sys/main}">主页</a>
        <span shiro:hasPermission="users:mgr">
            <a th:href="@{/sys/users}">用户管理</a>
        </span>
        <span shiro:hasPermission="roles:mgr">
            <a th:href="@{/sys/roles}">角色管理</a>
        </span>
        <span shiro:hasPermission="permissions:mgr">
            <a th:href="@{/sys/permissions}">权限管理</a>
        </span>
        <span shiro:hasPermission="products:mgr">
            <a th:href="@{/sys/products}">商品管理</a>
        </span>
        <span shiro:hasPermission="articles:mgr">
            <a th:href="@{/sys/articles}">图文管理</a>
        </span>
        <span>
            <a th:href="@{/logout}">注销</a>
        </span>
    </div>
    

    引入fragment:<div th:insert="~{common/head :: header}"></div>,shiro前端提示需要引入html命名空间:xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
    springboot+shiro实现自定义密码加密及验证(Bcrypt):https://blog.csdn.net/qq_21537671/article/details/107280447
    shiro注解:https://blog.csdn.net/qi923701/article/details/75224554/
    shiro thymeleaf:https://blog.csdn.net/qq_34579313/article/details/82024058
    swagger引用包

    <dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.5.10</version>
    </dependency>
    

    访问:http://localhost:8091/swagger-ui.html就可以看到所有接口,具体使用方法百度。

  • 相关阅读:
    事件聚合IEventAggregator和 Ihandle<T>
    基于.NET打造IP智能网络视频监控系统
    包图Package
    MVC与EasyUI结合增删改查
    Helper Method
    Moq & RhinoMocks
    Asp.net MVC集成Google Calendar API(附Demo源码)
    你可能不知道的跨域解决方案
    jQuery选择器大全
    关于Sqlite的一个demo
  • 原文地址:https://www.cnblogs.com/xsj1989/p/15131659.html
Copyright © 2011-2022 走看看