shiro+jwt进行认证和授权的解决方案代码实例
生活随笔
收集整理的這篇文章主要介紹了
shiro+jwt进行认证和授权的解决方案代码实例
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
文章目錄
- token類
- 自定義realm
- 自定義的jwtFilter用于訪問攔截:
- shiroconfig
- controller
- vo對(duì)象
- 測(cè)試
jwt和shiro框架就不多介紹了,直接上實(shí)例代碼吧。目前測(cè)試可正常登錄、獲得用戶角色、訪問接口時(shí)根據(jù)@requireRoles進(jìn)行攔截。
token類
import org.apache.shiro.authc.AuthenticationToken; //一般的登陸只需要校驗(yàn)賬號(hào)和密碼兩個(gè)要素,默認(rèn)的UsernamePasswordToken就能滿足需求 //這個(gè)就類似UsernamePasswordToken //在JwtRealm中的授權(quán)部分,可以使用JwtUtil.decode(jwt).get("username")獲取到username,使用username去數(shù)據(jù)庫中查找到對(duì)應(yīng)的權(quán)限,然后將權(quán)限賦值給這個(gè)用戶就可以實(shí)現(xiàn)權(quán)限的認(rèn)證了public class JWTToken implements AuthenticationToken {private String token;public JWTToken(String token){this.token = token;}@Overridepublic Object getPrincipal() {return token;}@Overridepublic Object getCredentials() {return token;} }自定義realm
@Slf4j public class JwtRealm extends AuthorizingRealm {/** 多重寫一個(gè)support* 標(biāo)識(shí)這個(gè)Realm是專門用來驗(yàn)證JwtToken* 不負(fù)責(zé)驗(yàn)證其他的token(UsernamePasswordToken)* */@Overridepublic boolean supports(AuthenticationToken token) {//這個(gè)token就是從過濾器中傳入的jwtTokenreturn token instanceof JWTToken;}@Autowiredprivate IInowRoleUserService roleUserService;@Autowiredprivate IInowRoleService roleService;@Autowiredprivate IInowUserService userService;//授權(quán)@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {log.info("=========執(zhí)行了授權(quán)===========================");InowUser userName = (InowUser) principalCollection.getPrimaryPrincipal();InowUser inowUser = userService.selectOneByName(userName.getUserName());SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();final InowRoleUser roleuser = roleUserService.getOne(new LambdaQueryWrapper<InowRoleUser>().eq(InowRoleUser::getUserId, inowUser.getUserId()));List<String> inowRoleList = new ArrayList<>();inowRoleList.add(roleService.getOne(new LambdaQueryWrapper<InowRole>().eq(InowRole::getId,roleuser.getRoleId())).getRoleName());simpleAuthorizationInfo.addRoles(inowRoleList);//添加用戶角色,用于@requireroles注解return simpleAuthorizationInfo;}//認(rèn)證// 這里的 token是從 JWTFilter 的 executeLogin 方法傳遞過來的(請(qǐng)求頭的token)// 只要調(diào)用了subject.login(token)方法,就會(huì)進(jìn)入到realm的doGetAuthenticationInfo內(nèi)。@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {String token = (String)auth.getCredentials();log.info("從auth獲得的Token"+token);String userName = JWTUtil.getUsername(token);//用戶不存在(這個(gè)在登錄時(shí)不會(huì)進(jìn)入,只有在token校驗(yàn)時(shí)才有可能進(jìn)入)if(userName == null){throw new UnknownAccountException();}InowUser user = userService.selectOneByName(userName);if(Objects.isNull(user)){throw new UnknownAccountException();}String salt = "666666"; //自己隨便寫的一個(gè)加密鹽Md5Hash md5Hash = new Md5Hash(user.getUserId(), salt, 1024); if(!JWTUtil.verify(token,userName, md5Hash.toHex()))//學(xué)號(hào)當(dāng)做用戶密碼{throw new IncorrectCredentialsException();}//toke過期if(JWTUtil.isExpire(token)){throw new ExpiredCredentialsException();}return new SimpleAuthenticationInfo(user, token, getName());}}自定義的jwtFilter用于訪問攔截:
@Slf4j //@Component("jwtFilter") //去掉@Component注解,讓filter不交給springbean管理,因?yàn)檫@會(huì)導(dǎo)致shiro內(nèi)置的anon過濾器失效,具體原因還未找出 public class JwtFilter extends BasicHttpAuthenticationFilter {/*** 進(jìn)行token的驗(yàn)證*/@Overrideprotected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {//在請(qǐng)求頭中獲取tokenHttpServletRequest httpServletRequest = (HttpServletRequest) request;String token = httpServletRequest.getHeader("Authorization"); //前端命名Authorization//token不存在if(token == null || "".equals(token)){ Result result = new Result();result.setCode(400);result.setMsg("token為空,無法訪問");out(response,result);return false;}//token存在,進(jìn)行驗(yàn)證JWTToken jwtToken = new JWTToken(token);try {SecurityUtils.getSubject().login(jwtToken); //通過subject,提交給myRealm進(jìn)行登錄驗(yàn)證return true;} catch (ExpiredCredentialsException e){Result result = new Result();result.setCode(400);result.setMsg("登錄已經(jīng)過期"); out(response,result);e.printStackTrace();return false;} catch (ShiroException e){// 其他情況拋出的異常統(tǒng)一處理,由于先前是登錄進(jìn)去的了,所以都可以看成是token被偽造造成的Result res = new Result();res.setMsg("無效token");out(response,res);e.printStackTrace();return false;}}/*** json形式返回結(jié)果token驗(yàn)證失敗信息,無需轉(zhuǎn)發(fā)*/private void out(ServletResponse response, Result res) throws IOException {HttpServletResponse httpServletResponse = WebUtils.toHttp(response);ObjectMapper mapper = new ObjectMapper();String jsonRes = mapper.writeValueAsString(res);httpServletResponse.setCharacterEncoding("UTF-8");httpServletResponse.setContentType("application/json; charset=utf-8");httpServletResponse.getOutputStream().write(jsonRes.getBytes());}/*** 過濾器攔截請(qǐng)求的入口方法*/@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {try {return executeLogin(request, response); //token驗(yàn)證} catch (Exception e) {e.printStackTrace();return false;}}/*** isAccessAllowed()方法返回false,即認(rèn)證不通過時(shí)進(jìn)入onAccessDenied方法*/ // @Override // protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { // return super.onAccessDenied(request, response); // }/*** token認(rèn)證executeLogin成功后,進(jìn)入此方法,可以進(jìn)行token更新過期時(shí)間*/ // @Override // protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {// } }shiroconfig
@Configuration public class ShiroConfig {public logOutFilter mylogoutFilter(){logOutFilter MylogoutFilter = new logOutFilter();MylogoutFilter.setLoginUrl("login");//設(shè)置退出后重定向的跳轉(zhuǎn)地址return MylogoutFilter;}/*** 注入Shiro過濾器鏈配置* 注入安全服務(wù)配置* 注入自定義jwt過濾器**/@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(@Autowired @Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager,@Autowired ShiroFilterChainDefinition definition){JwtFilter jwtFilter = new JwtFilter();ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();bean.setSecurityManager(defaultWebSecurityManager);//設(shè)置安全管理器//可以添加shiro的內(nèi)置過濾器//anon:無需認(rèn)證就可以訪問 authc:必須認(rèn)證了才能用 oerms:擁有對(duì)某個(gè)資源的權(quán)限才能訪問bean.setLoginUrl("/login");//設(shè)置登錄路徑//bean.setUnauthorizedUrl("/unlogin");Map<String, Filter> filterMap = new HashMap<>();// 注銷成功,則跳轉(zhuǎn)到指定頁面filterMap.put("logout", mylogoutFilter());filterMap.put("anon", new AnonymousFilter());filterMap.put("jwt",jwtFilter); bean.setFilters(filterMap);bean.setFilterChainDefinitionMap(definition.getFilterChainMap());return bean;}/*** 自定義jwt過濾器** @return*///@Beanpublic JwtFilter jwtFilter(){return new JwtFilter();}/*** 定義攔截器鏈,所有請(qǐng)求都經(jīng)過自定義的jwt過濾器** @return*/@Beanpublic ShiroFilterChainDefinition shiroFilterChainDefinition(){DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();Map<String,String> map = new LinkedHashMap<>();map.put("/login","anon");map.put("/swagger-ui.html","anon");map.put("/doc.html","anon");map.put("/**","jwt");definition.addPathDefinitions(map);return definition;}/*** 自定義Realm** @return*/@Beanpublic JwtRealm myUserRealm(){JwtRealm userRealm = new JwtRealm();return userRealm;}/*** 開啟認(rèn)證授權(quán)注解** @param securityManager* @return*/@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Autowired DefaultWebSecurityManager securityManager){AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}@Beanpublic LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}@Bean@DependsOn("lifecycleBeanPostProcessor")//DefaultAdvisorAutoProxyCreator://BeanPostProcessor實(shí)現(xiàn),它根據(jù)當(dāng)前BeanFactory中的所有候選Advisor創(chuàng)建 AOP 代理。public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();// 強(qiáng)制指定注解的底層實(shí)現(xiàn)使用 cglib 方案,防止重復(fù)代理和可能引起代理出錯(cuò)的問題defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);return defaultAdvisorAutoProxyCreator;}/*** 配置安全服務(wù)機(jī)制,注入自定義Realm,關(guān)閉了shiro的默認(rèn)session機(jī)制** @param* @return*//** 關(guān)閉shiro自帶的session,詳情見文檔* http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29*//** a. 告訴shiro不要使用默認(rèn)的DefaultSubject創(chuàng)建對(duì)象,因?yàn)椴荒軇?chuàng)建Session* */@Beanpublic SubjectFactory subjectFactory() {return new JwtDefaultSubjectFactory();}@Bean("securityManager")public DefaultWebSecurityManager securityManager(@Autowired JwtRealm myRealm) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(myRealm);// 關(guān)閉 ShiroDAO 功能DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();// 不需要將 Shiro Session 中的東西存到任何地方(包括 Http Session 中)defaultSessionStorageEvaluator.setSessionStorageEnabled(false);subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);securityManager.setSubjectDAO(subjectDAO);//禁止Subject的getSession方法securityManager.setSubjectFactory(subjectFactory());return securityManager;}}controller
@ApiOperation("登錄接口")@GetMappingpublic Result userLogin(@ApiParam(name="userId",value="用戶學(xué)號(hào)",required=true)@RequestParam(value = "userId",required = true)String userId,@ApiParam(name="userName",value="用戶姓名",required=true)@RequestParam(value = "userName",required = true)String userName) {// 1、獲取Subject實(shí)例對(duì)象//根據(jù)用戶名獲取正確用戶信息InowUser user = userService.selectOneByName(userName);if(user == null){return Result.fail("無效用戶,請(qǐng)檢查您的姓名");}//鹽 + 輸入的密碼(注意不是用戶的正確密碼) + 1024次散列,作為token生成的密鑰String salt = "666666";Md5Hash md5Hash = new Md5Hash(userId, salt, 1024);//生成token字符串String token = JWTUtil.getJwtToken(userName, md5Hash.toHex()); //toHex轉(zhuǎn)換成16進(jìn)制,32為字符log.info("從controller生成的Token"+token);JWTToken jwtToken = new JWTToken(token);Subject currentUser = SecurityUtils.getSubject();// 4、認(rèn)證try {currentUser.login(jwtToken);// 傳到Realm類中的方法進(jìn)行認(rèn)證 進(jìn)入 AuthenticationInfo方法//session.setAttribute("username", userName);InowUser nowUser = (InowUser) currentUser.getPrincipal();InowUserVo userVo = new InowUserVo();BeanUtils.copyProperties(nowUser,userVo);userVo.setToken(token);return Result.success(userVo);}catch (AuthenticationException e) {String msg = null;if (StringUtils.isNotEmpty(e.getMessage())) {msg = e.getMessage();}return Result.fail("用戶名與學(xué)號(hào)不匹配!");}// }}vo對(duì)象
在vo中放了token屬性的成員屬性
@Data @JsonInclude(JsonInclude.Include.NON_EMPTY) public class InowUserVo implements Serializable {private static final long serialVersionUID = 1L;/** 學(xué)號(hào)或者教師id */@NotBlank(message = "id不能為空!")@ApiModelProperty(value="用戶id,學(xué)號(hào)或教師號(hào)",name="id",example="2020214283")private String userId;private Integer id;/** 姓名 */@NotBlank(message = "姓名不能為空!")@ApiModelProperty(value="用戶姓名",name="userName",example="張三")private String userName;/** 學(xué)院 */@ApiModelProperty(value="用戶學(xué)院",name="userAcademy",example="計(jì)算機(jī)與信息學(xué)院")private String userAcademy;/** 微信id */@ApiModelProperty(value="微信id",name="wechatId",example="微信id")private Long wechatId;@ApiModelProperty(value="性別",name="userSex",example="1男,2女")private byte userSex;@ApiModelProperty(value="最后登陸時(shí)間",name="loginDate")private Date loginDate;@ApiModelProperty(value="用戶角色",name="roleName")private String roleName; private String token; }測(cè)試
登錄接口:
訪問一個(gè)標(biāo)有注解@RequireRoles(“admin”)的方法:
在不加token時(shí)返回結(jié)果:
加一個(gè)沒有Admin角色的Token訪問結(jié)果:
用有admin角色的用戶token訪問結(jié)果:
總結(jié)
以上是生活随笔為你收集整理的shiro+jwt进行认证和授权的解决方案代码实例的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nacos+openfeign服务提供和
- 下一篇: 【java读书笔记】ThreadGrou