spring整合shiro系列(二)SSM整合shiro

前言

在之前我們已經對於Shiro這個安全管理框架有了一定的認識,同時也已經在SSM框架環境下對於Shiro進行了配置,下面我們來看看如何具體使用Shiro進行開發。

SSM框架

這裏簡單的說一下,因爲這裏使用的是Spring+Spring MVC+Mybatis框架,我是之前就搭好的。可能有些同學喜歡用Spring Boot來整合,這也是沒有問題的,後面有空會把Spring Boot的整合放上來。回到SSM框架上,這個應該算是java中比較經典的一個框架組合,會的同學不用說自己可以搭建,如果有不太清楚SSM的後面準備再寫一篇SSM框架的,大家一起學習一下。

下面是我項目的總體結構

還是中規中矩的Web層-Serivce層-Dao層,spring配置文件我根據不同的功能分開放置這樣更加清晰。

然後是Shiro的兩個文件,一個是自定義的Realm,用於驗證用戶和授予權限的;另一個是進行加密的。

開發中先寫一個註冊的功能用於添加用戶,簡單一點的話就是需要用戶名,密碼(密碼這裏用MD5+鹽)。這兩點是基本的。

下面是代碼的一個片段,這裏我是通過UUID生成了一個鹽對原密碼加密,然後就用到我們之前的加密用的文件

User user = new User();
        String salt = UUIDUtil.createUUID().toString();
        Date createTime = new Date();
        Integer state = 0;
        user.setUserName(userName);
        user.setPassword(password);
        user.setSalt(salt);
        user.setCreateTime(createTime);
        user.setState(state);
        User newUser = passWordHelper.encryptPassword(user);
@Component
public class PassWordHelper {

    private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();

    @Value("MD5")
    private String algorithmName;
    @Value("2")
    private int hashIterations;

    public void setRandomNumberGenerator(RandomNumberGenerator randomNumberGenerator) {
        this.randomNumberGenerator = randomNumberGenerator;
    }

    public void setAlgorithmName(String algorithmName) {
        this.algorithmName = algorithmName;
    }

    public void setHashIterations(int hashIterations) {
        this.hashIterations = hashIterations;
    }

    public User encryptPassword(User user){
        if(user.getSalt() == null || "".equals(user.getSalt())){
            user.setSalt(randomNumberGenerator.nextBytes().toHex());
        }
        String newPassword = new SimpleHash(algorithmName,user.getPassword(),
                ByteSource.Util.bytes(user.getSalt()),hashIterations).toHex();
        user.setPassword(newPassword);
        return user;
    }

}

這裏引用了加密方式和加密次數兩個參數,然後通過SimpleHash進行加密,得到一個加密後的密碼放入user對象中,然後在Serivce層中將新的user傳入Dao層添加到數據庫。ok,現在我們已經有一個用戶了,你也可以用別的方法,只要保證先添加一個用戶進來就對了。

下面來看一下Realm文件

 

public class myRealm extends AuthorizingRealm{

    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private PermissionService permissionService;

    /**
     * @description 爲當前登錄的用戶授予角色和權限
     * @author zhou
     * @created  2018/10/17 15:15
     * @param
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //獲取用戶名
        String userName = (String)principalCollection.getPrimaryPrincipal();
        Session session = SecurityUtils.getSubject().getSession();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //查詢用戶的role
        Set<String> roles = roleService.findRoleByUserName(userName);
        authorizationInfo.setRoles(roles);

        //查詢用戶的permission
        Set<String> permissions = permissionService.findPermissionByUserName(userName);
        authorizationInfo.setStringPermissions(permissions);
        return authorizationInfo;
    }

    /**
     * @description 驗證當前登錄的用戶
     * @author zhou
     * @created  2018/10/17 15:16
     * @param
     * @return
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws
            AuthenticationException {
        //獲得用戶信息
        String userName = (String)authenticationToken.getPrincipal();
        //從數據庫中查找
        User user = userService.findByUserName(userName);
        if(user==null){
            //賬號不存在
            throw new UnknownAccountException();
        }else if(user.getState() == 1){
            //賬號鎖定
            throw new LockedAccountException();
        }
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user.getUserName(),
                user.getPassword(), ByteSource.Util.bytes(user.getSalt()),getName());
        return simpleAuthenticationInfo;
    }
}

首先新建一個自定義的Realm,然後繼承AuthorizingRealm,重寫其中的doGetAuthorizationInfo和doGetAuthenticationInfo兩個方法。

首先看一下授權的方法,這裏是通過用戶名去我們的數據庫中查詢該用戶的角色和權限,這裏用set集合去存放角色和權限。然後把這兩個信息存到authorizationInfo中,這樣在用戶被授權後,如果用戶要執行什麼操作,可以從中獲取數據來判斷用戶是否具有對應的權限。

然後是登錄驗證的方法,這個和平時寫的javaWeb的驗證方式比較類似,首先獲得用戶名去數據庫中查找用戶信息,然後做一些簡單的校驗比如是否存在用戶和用戶是否被鎖定等等。然後將用戶名,密碼,鹽和Realm的名稱。然後在登錄的時候會用到這些。下面是我的登錄代碼

 @RequestMapping(value = "/login")
    public WebResponse userLogin(@RequestParam("userName") String userName,@RequestParam("password") String password,
                                 @RequestParam(value = "rememberMe",defaultValue = "false") boolean rememberMe,
                                         HttpServletRequest request){
        if(isEmpty(userName)||isEmpty(password)){
            log.error("用戶名或密碼爲空");
            return new WebResponse().error(401,null,"用戶名或密碼爲空");
        }
        //查詢角色
        Set<String> roleList = roleService.findRoleByUserName(userName);
        //查詢權限
        Set<String> permissionList = permissionService.findPermissionByUserName(userName);
        //獲得主體
        Subject currentUser = SecurityUtils.getSubject();
        //判斷用戶是否登陸
        if(!currentUser.isAuthenticated()) {
            UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
            try {
                //token.setRememberMe(rememberMe);
                //存入參數
                request.getSession().setAttribute("token",userName);
                request.getSession().setAttribute("role",roleList);
                request.getSession().setAttribute("permission",permissionList);
                currentUser.login(token);
            } catch (UnknownAccountException uae) {
                log.error("賬號不存在");
                return new WebResponse().error(402, null, "賬號不存在");
            } catch (IncorrectCredentialsException ice) {
                log.error("密碼錯誤,請重試");
                return new WebResponse().error(403, null, "密碼錯誤,請重試");
            } catch (LockedAccountException lae) {
                log.error("該賬號已被禁用,無法登陸");
                return new WebResponse().error(404, null, "該賬號已被禁用,無法登陸");
            } catch (AuthenticationException ae) {
                log.error("未知錯誤");
                return new WebResponse().error(405, null, "未知錯誤");
            }
        }
        HashMap<String,Object> userMap = new HashMap<>();
        userMap.put("msg",userName + "登陸成功");
        userMap.put("role",roleList);
        userMap.put("permission",permissionList);
        return new WebResponse().ok(userMap);
    }

這裏主要看這個login()方法,當程序判定該用戶未被認證時,將用戶名和密碼放入Token中然後調用login,此時就會用到之前realm中用戶驗證的方法,將用戶輸入的信息和數據庫中查到的信息進行比對校驗。

後面是一些捕獲異常比如賬號不存在,密碼錯誤,賬號鎖定等等。

以上就是Shiro權限登錄的一個流程和整合過程中所需要的文件。

不過就個人覺得由於Shiro框架封裝程度比較高,感覺看似隨意調用了幾個方法就實現了驗證和授權,其實底層做的工作和javaWeb中做的是類似的。後面有機會會從源碼的角度再次剖析一下Shiro的Realm的工作原理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章