Spring Boot Shro

14126 ワード

シンロコアAPI
Subject:    (        Subject)。

    principal:    。      、  、      ,             。

    credential:    。      ,     。

SecurityManager:     (   Realm),      。

Realm:Shiro        。


Cryptography:  ,        ,           ,       。

Caching:  ,       ,     、     /        ,        。

CacheManager:     ,      、  、         。

SessionManager:    ,            ,       ,           。

SessionDAO:    。
Shro認証と授権
    :

    Step1:         Subject.login(token)    ,            AuthenticationToken    Token。

    Step2:  Subject            SecurityManager(Shiro      )          。

    Step3、4、5:SecurityManager       Realm       。

    (      :   、       ):

      (Permission):        (  、  、  、       )。
    
      (Role):          ,           。
    
      (User):  Shiro  ,         ,       Subject     。


    :

                 , redis       session,       。
    
                    ,      。

            session  sessionId,         ,    cookie  ,   session   redis 。
    
             ,    session     expireTime。
    
             ,           cookie   request headers    。
         sessionId     redis     。
          ,        ,      ,      。
Spring Boot Shro依存

    org.apache.shiro
    shiro-spring
カスタムRealm
    Realm      AuthorizingRealm  ,         ,     Realm  。

        :
    doGetAuthenticationInfo()   :           ,      。
    doGetAuthorizationInfo()   :                   。

public class CustomRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    /**
     *                 
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //     
        String account = (String) principals.getPrimaryPrincipal();

        //            
        User user = userService.getUserByAccount(account);

        //    
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        Set roles = new HashSet();
        if (user.getAdmin()) {
            roles.add(Base.ROLE_ADMIN);
        }
        authorizationInfo.setRoles(roles);

        return authorizationInfo;
    }

    /**
     *       
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //     
        String account = (String) token.getPrincipal();

        //         
        User user = userService.getUserByAccount(account);

        if (null == user) {
            throw new UnknownAccountException();    //      
        }
        if (UserStatus.blocked.equals(user.getStatus())) {
            throw new LockedAccountException(); //    
        }

        //              ,       
        return new SimpleAuthenticationInfo(
                user.getAccount(),
                user.getPassword(),
                ByteSource.Util.bytes(user.getSalt()),  // (        )
                getName()
        );
    }
}
カスタムSession DAO
Cachemanager              、           ,    Session  。

SessionDAO  Session    ,    Redis   。

   SessionDAO:MemorySessionDAO:

     Session      ,     ConcurrentHashMap。

    public class MemorySessionDAO extends AbstractSessionDAO {

        private ConcurrentMap sessions = new ConcurrentHashMap();

        protected Serializable doCreate(Session session) {
            Serializable sessionId = this.generateSessionId(session);
            this.assignSessionId(session, sessionId);
            this.storeSession(sessionId, session);
            return sessionId;
        }
    }


/**
 *  Session   Redis
 */
public class CustomSessionDAO extends CachingSessionDAO {

    @Autowired
    private RedisTemplate redisTemplate;

    //        :30  
    public final static long DEFAULT_EXPIRE = 60 * 30;

    @Override
    protected Serializable doCreate(Session session) {
        //  SessionId
        Serializable sessionId = generateSessionId(session);
        //  SessionId
        assignSessionId(session, sessionId);
        //  Session
        redisTemplate.opsForValue().set(sessionId.toString(), session, DEFAULT_EXPIRE, TimeUnit.SECONDS);
        return sessionId;
    }

    @Override
    protected void doUpdate(Session session) {
        if (session instanceof ValidatingSession && !((ValidatingSession) session).isValid()) {
            //    /  
            return;
        }
        redisTemplate.opsForValue().set(session.getId().toString(), session, DEFAULT_EXPIRE, TimeUnit.SECONDS);
    }

    @Override
    protected void doDelete(Session session) {
        redisTemplate.delete(session.getId().toString());
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        return (Session) redisTemplate.opsForValue().get(sessionId.toString());
    }
}
カスタムSession Manager(完璧を待つ)
public class CustomSessionManager extends DefaultWebSessionManager {

    public static final String TOKEN = "token";

    /**
     *          ,   token 。
     *      ,   token,      request 。
     *            ,   request    ,        。
     *          token  ,         cookie   ,        。
     */
    @Override
    public Serializable getSessionId(SessionKey key) {
        Serializable sessionId = key.getSessionId();
        if(sessionId == null && WebUtils.isWeb(key)){
            HttpServletRequest request = WebUtils.getHttpRequest(key);
            HttpServletResponse response = WebUtils.getHttpResponse(key);
            sessionId = this.getSessionId(request,response);
        }
        HttpServletRequest request = WebUtils.getHttpRequest(key);
        request.setAttribute(TOKEN,sessionId.toString());
        return sessionId;
    }

    /**
     * DefaultWebSessionManager     ,   Cookie  SessionId。
     *    ,      SessionId       request header     。
     */
    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String id = httpRequest.getHeader(TOKEN);

        if (!StringUtils.isEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        }
        return super.getSessionId(request, response);
    }
}
プロファイル
@Configuration
public class ShiroConfig {

    /**
     *    ShiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        
        //  securityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //LinkedHashMap     ,         
        Map filterMap = new LinkedHashMap();
        filterMap.put("/static/**", "anon");    //        
        filterMap.put("/login", "anon");
        filterMap.put("/register", "anon");

        //       ,         Shiro        ,        LoginUrl
        filterMap.put("/logout", "logout");

        filterMap.put("/**/create", "authc");   //         
        filterMap.put("/**/update", "authc");
        filterMap.put("/**/delete", "authc");
        filterMap.put("/upload", "authc");

        filterMap.put("/admin", "perms[admin]");    //              ,      :perms["admin,user"]
        filterMap.put("/admin", "role[admin]"); //              

        filterMap.put("/**", "anon");


        //       URL,          URL
        shiroFilterFactoryBean.setLoginUrl("/login");
        //              URL
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //       ,          URL
        shiroFilterFactoryBean.setUnauthorizedUrl("/unAuthorized");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        return shiroFilterFactoryBean;
    }

    /**
     *        :DefaultWebSecurityManager
     */
    @Bean
    public SecurityManager securityManager(CustomRealm realm, SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }

    /**
     *       Realm
     */
    @Bean
    public CustomRealm customRealm() {
        CustomRealm shiroRealm = new CustomRealm();
        //      
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("md5");    //    
        matcher.setHashIterations(2);   //    
        shiroRealm.setCredentialsMatcher(matcher);
        return shiroRealm;
    }

    /**
     *    SessionManager
     */
    @Bean
    public SessionManager sessionManager() {
        CustomSessionManager customSessionManager = new CustomSessionManager();
        //session    :1  (     )
        customSessionManager.setGlobalSessionTimeout(60 * 60 * 1000);
        customSessionManager.setSessionDAO(new CustomSessionDAO());
        return customSessionManager;
    }

    /**
     * Spring Boot Shiro     
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
Sessionのクエリ、更新
SimpleSession    :

    Serializable id:session id;

    Date startTimestamp:session     ;

    Date stopTimestamp:session     ;

    Date lastAccessTime:session         ,    startTimestamp

    long timeout:session     ,  30  

    boolean expired:session    

    Map attributes:session     


  :Session session = SecurityUtils.getSubject().getSession();   //          subjuct session。

  :SimpleSession touch()

    public void touch() {
        this.lastAccessTime = new Date();
    }

    Web  ,    ShiroFilter      session.touch()         。

          :    -lastAccessTime=        。
Shroは3つの方式の授権をサポートします.
1、   ,   if/else     

    Subject subject = SecurityUtils.getSubject();
    if(subject.hasRole("admin")) {
        //    ,      
    } else {
        //    ,     
    }

2、   ,      Java            

    @RequiresPermissions("admin")
    public List listUser() {
        //    ,      
    }

3、JSP/GSP  , JSP/GSP           

    
        
    
Spring Boot統合ShroとEhcache(完璧にする予定)
       ehcache.xml:

    application.xml      :spring.cache.ehcache.config=classpath:ehcache.xml

    
    
        
        

        
     
        
    
ControllerとServiceは使用します.
Controller:

    @PostMapping("/login")
    public Result login(@RequestBody User user) {
        Result r = new Result();
        //  Subject    
        Subject subject = SecurityUtils.getSubject();
        //      
        UsernamePasswordToken token = new UsernamePasswordToken(user.getAccount(), user.getPassword());
        try {
            //      (  UserRealm      )
            subject.login(token);
            //    
            User currentUser = userService.getUserByAccount(user.getAccount());
            subject.getSession().setAttribute(Base.CURRENT_USER, currentUser);
            r.setResultCode(ResultCode.SUCCESS);
            r.getData().put("token", subject.getSession().getId());
        } catch (UnknownAccountException e) {
            r.setResultCode(ResultCode.USER_NOT_EXIST); //     
        } catch (LockedAccountException e) {
            r.setResultCode(ResultCode.USER_ACCOUNT_FORBIDDEN); //     
        }catch (IncorrectCredentialsException e) {
            r.setResultCode(ResultCode.USER_LOGIN_PASSWORD_ERROR);   //    
        } catch (AuthenticationException e) {
            r.setResultCode(ResultCode.USER_LOGIN_ERROR);   //    (      )
        }
        return r;
    }

    @PostMapping("/register")
    public Result register(@RequestBody User user) {
        Result r = new Result();

        User temp = userService.getUserByAccount(user.getAccount());
        if (null != temp) {
            r.setResultCode(ResultCode.USER_HAS_EXISTED);
            return r;
        }

        userService.saveUser(user);

        r.setResultCode(ResultCode.SUCCESS);
        return r;
    }

Service:

    @Override
    @Transactional
    public void saveUser(User user) {
        //    
        String newPassword = new SimpleHash(
                "md5",user.getPassword(),
                ByteSource.Util.bytes("salt"),2).toHex();

        user.setPassword(newPassword);

        return userRepository.save(user);
    }