集成shiro权限管理

2019-07-11  本文已影响0人  Liopisces

Step 1. 添加依赖

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.4.0</version>
        </dependency>

Step 2. 权限类实体(ShiroUserDomain)

public class ShiroUserDomain implements Serializable {

    private Integer id;
    private String name;
    private String password;
    private String perms;
//省略GET、SET方法

Step 3. 添加DAO(ShiroUserDao)

@Mapper
public interface ShiroUserDao {

    @Select("select * from shiro_user where name = #{name}")
    public ShiroUserDomain findByName(String name);

    @Select("select * from shiro_user where id = #{id}")
    public ShiroUserDomain findById(Integer id);
}

Step 4. 添加Service

public interface ShiroUserService {

    ShiroUserDomain findByName(String name);
    ShiroUserDomain findById(Integer id);
}
@Service
public class ShiroUserServiceImpl implements ShiroUserService {

    @Autowired
    private ShiroUserDao shiroUserDao;

    @Override
    public ShiroUserDomain findByName(String name) {
        return shiroUserDao.findByName(name);
    }

    @Override
    public ShiroUserDomain findById(Integer id) {
        return shiroUserDao.findById(id);
    }
}

Step 5. 添加Realm

@Autowired
    private ShiroUserService shiroUserService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权逻辑");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Subject subject = SecurityUtils.getSubject();
        ShiroUserDomain user = (ShiroUserDomain) subject.getPrincipal();
        ShiroUserDomain dbUser = shiroUserService.findById(user.getId());
        info.addStringPermission(dbUser.getPerms());
        return info;

    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证逻辑");

        UsernamePasswordToken token  =  (UsernamePasswordToken)authenticationToken;

        ShiroUserDomain user = shiroUserService.findByName(token.getUsername());

        //1、判断用户名
        if(user == null){
            //用户名不存在
            return null;
            //shiro底层会抛出UnKnowAccountException
        }

        //2、判断密码, 这里的user是principal
        return new SimpleAuthenticationInfo(user,user.getPassword(),getName());
    }

Step 6. 添加ShiroConfig

@Configuration
public class ShiroConfig {
    @Autowired
    private ShiroUserService shiroUserService;

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 添加Shiro内置过滤器
        /**
         * Shiro内置过滤器,可以实现权限相关的拦截器
         *  常用的过滤器:
         *      anon: 无需认证(登录)可以访问
         *      authc: 必须认证才可以访问
         *      user: 如果使用rememberMe功能可以直接访问
         *      perms: 该资源必须得到资源权限才可以访问
         *      role: 该资源必须得到角色权限才可以访问
         */

        Map<String, String> filerMap = new LinkedHashMap<>(); //顺序的map
        //配置记住我或认证通过可以访问的地址

        filerMap.put("/studentInfo","perms[user:student]");
        filerMap.put("/user/logout","authc");


        //设置登录的页面,发送toLogin请求
        shiroFilterFactoryBean.setLoginUrl("/");

        //设置未授权的页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");
        //设置过滤器
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filerMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 创建DefaultWebSecurityManager
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")ShiroUserRealm shiroUSerRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 关联realm
        securityManager.setRealm(shiroUSerRealm);
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }

    /**
     * 创建Realm
     */
    @Bean(name = "userRealm")
    public ShiroUserRealm getRealm(){
        return new ShiroUserRealm();
    }

    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }

    /**
     2   * cookie对象;
     3   * rememberMeCookie()方法是设置Cookie的生成模版,比如cookie的name,cookie的有效时间等等。
     4   * @return
     5  */
    @Bean
    public SimpleCookie rememberMeCookie(){
        //System.out.println("ShiroConfiguration.rememberMeCookie()");
        //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //<!-- 记住我cookie生效时间30天 ,单位秒;-->
        simpleCookie.setMaxAge(259200);
        return simpleCookie;
    }

    /**
     * cookie管理对象;
     * rememberMeManager()方法是生成rememberMe管理器,而且要将这个rememberMe管理器设置到securityManager中
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager(){
        //System.out.println("ShiroConfiguration.rememberMeManager()");
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
        return cookieRememberMeManager;
    }
}

Step 7. 编写Controller

@Controller
@Api("登录相关接口")
public class LoginControll {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoginControll.class);

    @Autowired
    UserService userService;

    @ApiOperation(value = "登录", httpMethod = "POST", response = String.class)
    @PostMapping("/user/login")
    public String toLogin(
            HttpServletRequest request,
            HttpServletResponse response,
            @RequestParam(name = "username", required = true)
                    String username,
            @RequestParam(name = "password", required = true)
                    String password,
            Model model,
            HttpSession session
    ) {
//        String pwd = utils.MD5encode(username + password);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);

        try {
            subject.login(token);
            session.setAttribute("loginUser", username);
            return "redirect:/main.html";
        } catch (UnknownAccountException e) {
            model.addAttribute("msg","用户名不存在");
            return "login";

        }catch (IncorrectCredentialsException e){
            model.addAttribute("msg","密码错误");
            return "login";
        }

    }

    @ApiOperation(value = "登出", httpMethod = "GET")
    @GetMapping(value = "/user/logout")
    public void toLogout(HttpServletRequest request,
                         HttpServletResponse response
    ) {
        request.getSession().removeAttribute("loginUser");
        try {
            response.sendRedirect("/");
        } catch (IOException e) {
            e.printStackTrace();
            LOGGER.error("注销失败", e);
        }
    }

    @ApiOperation(value = "无权限访问", httpMethod = "GET")
    @GetMapping("/noAuth")
    public String noAuth(){
        return "noAuth";
    }

    @GetMapping("/test")
    public String shouye(){
        return "login";
    }
}

Step 8. shiro权限控制的三种方式

  1. 编程方式:通过java代码
public String logon(Model model) {
        Subject subject = SecurityUtils.getSubject();

        if (subject.hasRole("xxx")) {
            return "logon";
        }
        model.addAttribute("msg","没有角色xxx");
        return "login";
    }
  1. JSP标签

<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.w3.org/1999/xhtml">

<shiro:authenticated> <p>登录之后</p></shiro:authenticated>   
<shiro:notAuthenticated> 未登录</shiro:notAuthenticated><input type="button" name="dianji" id="dianji"onclick="login()"/>
<shiro:hasRole name="manager">manager角色</shiro:hasRole>
<shiro:hasRole name="admin">我admin角色</shiro:hasRole>
<shiro:hasRole name="admins">我有admins角色  </shiro:hasRole>
<shiro:lacksRole name="user">没有user角色</shiro:lacksRole>
<shiro:hasPermission name="manager"><p>你有权限看到此处!</p></shiro:hasPermission>
  1. 通过注解方式:放在controller 方法头上(但是之前放在service可行,后来不行了,不知道为什么)
//shiro权限校验
//@RequiresRoles({"xcxcxc"})//角色校验
@RequiresPermissions({"xcxcxc"})//权限校验
@RequestMapping("/queryUser") 
@ResponseBody

建议别使用第三种方式,有很多问题。比如说:注解无效,影响其他注解@Cache无效,发生异常无法跳转等,很麻烦!

上一篇 下一篇

猜你喜欢

热点阅读