电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術(shù)文章
文章詳情頁

SpringBoot集成SpringSecurity和JWT做登陸鑒權(quán)的實現(xiàn)

瀏覽:20日期:2023-05-22 17:39:39

廢話

目前流行的前后端分離讓Java程序員可以更加專注的做好后臺業(yè)務邏輯的功能實現(xiàn),提供如返回Json格式的數(shù)據(jù)接口就可以。SpringBoot的易用性和對其他框架的高度集成,用來快速開發(fā)一個小型應用是最佳的選擇。

一套前后端分離的后臺項目,剛開始就要面對的就是登陸和授權(quán)的問題。這里提供一套方案供大家參考。

主要看點:

登陸后獲取token,根據(jù)token來請求資源 根據(jù)用戶角色來確定對資源的訪問權(quán)限 統(tǒng)一異常處理 返回標準的Json格式數(shù)據(jù)

正文

首先是pom文件:

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--這是不是必須,只是我引用了里面一些類的方法--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-solr</artifactId> </dependency><!--這是不是必須,只是我引用了里面一些類的方法--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.6.1</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.6.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> <version>1.0.9.RELEASE</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>

application.yml:

spring : datasource : url : jdbc:mysql://127.0.0.1:3306/les_data_center?useUnicode=true&amp;characterEncoding=UTF-8&allowMultiQueries=true&useAffectedRows=true&useSSL=false username : root password : 123456 driverClassName : com.mysql.jdbc.Driver jackson: data-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8mybatis : config-location : classpath:/mybatis-config.xml# JWTjwt: header: Authorization secret: mySecret #token有效期一天 expiration: 86400 tokenHead: 'Bearer '

接著是對security的配置,讓security來保護我們的API

SpringBoot推薦使用配置類來代替xml配置。那這里,我也使用配置類的方式。

@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true)public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final JwtAuthenticationEntryPoint unauthorizedHandler; private final AccessDeniedHandler accessDeniedHandler; private final UserDetailsService CustomUserDetailsService; private final JwtAuthenticationTokenFilter authenticationTokenFilter; @Autowired public WebSecurityConfig(JwtAuthenticationEntryPoint unauthorizedHandler, @Qualifier('RestAuthenticationAccessDeniedHandler') AccessDeniedHandler accessDeniedHandler, @Qualifier('CustomUserDetailsService') UserDetailsService CustomUserDetailsService, JwtAuthenticationTokenFilter authenticationTokenFilter) { this.unauthorizedHandler = unauthorizedHandler; this.accessDeniedHandler = accessDeniedHandler; this.CustomUserDetailsService = CustomUserDetailsService; this.authenticationTokenFilter = authenticationTokenFilter; } @Autowired public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { authenticationManagerBuilder// 設置UserDetailsService.userDetailsService(this.CustomUserDetailsService)// 使用BCrypt進行密碼的hash.passwordEncoder(passwordEncoder()); } // 裝載BCrypt密碼編碼器 @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity.exceptionHandling().accessDeniedHandler(accessDeniedHandler).and()// 由于使用的是JWT,我們這里不需要csrf.csrf().disable().exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()// 基于token,所以不需要session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 對于獲取token的rest api要允許匿名訪問.antMatchers('/api/v1/auth', '/api/v1/signout', '/error/**', '/api/**').permitAll()// 除上面外的所有請求全部需要鑒權(quán)認證.anyRequest().authenticated(); // 禁用緩存 httpSecurity.headers().cacheControl(); // 添加JWT filter httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers('/v2/api-docs','/swagger-resources/configuration/ui','/swagger-resources','/swagger-resources/configuration/security','/swagger-ui.html' ); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); }}

該類中配置了幾個bean來供security使用。

JwtAuthenticationTokenFilter:token過濾器來驗證token有效性 UserDetailsService:實現(xiàn)了DetailsService接口,用來做登陸驗證 JwtAuthenticationEntryPoint :認證失敗處理類 RestAuthenticationAccessDeniedHandler: 權(quán)限不足處理類

那么,接下來一個一個實現(xiàn)這些類:

/** * token校驗,引用的stackoverflow一個答案里的處理方式 * Author: JoeTao * createAt: 2018/9/14 */@Componentpublic class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Value('${jwt.header}') private String token_header; @Resource private JWTUtils jwtUtils; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String auth_token = request.getHeader(this.token_header); final String auth_token_start = 'Bearer '; if (StringUtils.isNotEmpty(auth_token) && auth_token.startsWith(auth_token_start)) { auth_token = auth_token.substring(auth_token_start.length()); } else { // 不按規(guī)范,不允許通過驗證 auth_token = null; } String username = jwtUtils.getUsernameFromToken(auth_token); logger.info(String.format('Checking authentication for user %s.', username)); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { User user = jwtUtils.getUserFromToken(auth_token); if (jwtUtils.validateToken(auth_token, user)) {UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));logger.info(String.format('Authenticated user %s, setting security context', username));SecurityContextHolder.getContext().setAuthentication(authentication); } } chain.doFilter(request, response); }}

/** * 認證失敗處理類,返回401 * Author: JoeTao * createAt: 2018/9/20 */@Componentpublic class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { private static final long serialVersionUID = -8970718410437077606L; @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { //驗證為未登陸狀態(tài)會進入此方法,認證錯誤 System.out.println('認證失敗:' + authException.getMessage()); response.setStatus(200); response.setCharacterEncoding('UTF-8'); response.setContentType('application/json; charset=utf-8'); PrintWriter printWriter = response.getWriter(); String body = ResultJson.failure(ResultCode.UNAUTHORIZED, authException.getMessage()).toString(); printWriter.write(body); printWriter.flush(); }}

因為我們使用的REST API,所以我們認為到達后臺的請求都是正常的,所以返回的HTTP狀態(tài)碼都是200,用接口返回的code來確定請求是否正常。

/*** 權(quán)限不足處理類,返回403 * Author: JoeTao * createAt: 2018/9/21 */@Component('RestAuthenticationAccessDeniedHandler')public class RestAuthenticationAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException { //登陸狀態(tài)下,權(quán)限不足執(zhí)行該方法 System.out.println('權(quán)限不足:' + e.getMessage()); response.setStatus(200); response.setCharacterEncoding('UTF-8'); response.setContentType('application/json; charset=utf-8'); PrintWriter printWriter = response.getWriter(); String body = ResultJson.failure(ResultCode.FORBIDDEN, e.getMessage()).toString(); printWriter.write(body); printWriter.flush(); }}

/** * 登陸身份認證 * Author: JoeTao * createAt: 2018/9/14 */@Component(value='CustomUserDetailsService')public class CustomUserDetailsService implements UserDetailsService { private final AuthMapper authMapper; public CustomUserDetailsService(AuthMapper authMapper) { this.authMapper = authMapper; } @Override public User loadUserByUsername(String name) throws UsernameNotFoundException { User user = authMapper.findByUsername(name); if (user == null) { throw new UsernameNotFoundException(String.format('No user found with username ’%s’.', name)); } Role role = authMapper.findRoleByUserId(user.getId()); user.setRole(role); return user; }}

登陸邏輯:

public ResponseUserToken login(String username, String password) { //用戶驗證 final Authentication authentication = authenticate(username, password); //存儲認證信息 SecurityContextHolder.getContext().setAuthentication(authentication); //生成token final User user = (User) authentication.getPrincipal();// User user = (User) userDetailsService.loadUserByUsername(username); final String token = jwtTokenUtil.generateAccessToken(user); //存儲token jwtTokenUtil.putToken(username, token); return new ResponseUserToken(token, user); }private Authentication authenticate(String username, String password) { try { //該方法會去調(diào)用userDetailsService.loadUserByUsername()去驗證用戶名和密碼,如果正確,則存儲該用戶名密碼到“security 的 context中” return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); } catch (DisabledException | BadCredentialsException e) { throw new CustomException(ResultJson.failure(ResultCode.LOGIN_ERROR, e.getMessage())); } }

自定義異常:

@Getterpublic class CustomException extends RuntimeException{ private ResultJson resultJson; public CustomException(ResultJson resultJson) { this.resultJson = resultJson; }}

統(tǒng)一異常處理:

/** * 異常處理類 * controller層異常無法捕獲處理,需要自己處理 * Created by jt on 2018/8/27. */@RestControllerAdvice@Slf4jpublic class DefaultExceptionHandler { /** * 處理所有自定義異常 * @param e * @return */ @ExceptionHandler(CustomException.class) public ResultJson handleCustomException(CustomException e){ log.error(e.getResultJson().getMsg().toString()); return e.getResultJson(); }}

所有經(jīng)controller轉(zhuǎn)發(fā)的請求拋出的自定義異常都會被捕獲處理,一般情況下就是返回給調(diào)用方一個json的報錯信息,包含自定義狀態(tài)碼、錯誤信息及補充描述信息。

值得注意的是,在請求到達controller之前,會被Filter攔截,如果在controller或者之前拋出的異常,自定義的異常處理器是無法處理的,需要自己重新定義一個全局異常處理器或者直接處理。

Filter攔截請求兩次的問題

跨域的post的請求會驗證兩次,get不會。網(wǎng)上的解釋是,post請求第一次是預檢請求,Request Method: OPTIONS。解決方法:

在webSecurityConfig里添加

.antMatchers(HttpMethod.OPTIONS, '/**').permitAll()

就可以不攔截options請求了。

這里只給出了最主要的代碼,還有controller層的訪問權(quán)限設置,返回狀態(tài)碼,返回類定義等等。

所有代碼已上傳GitHub,項目地址

到此這篇關(guān)于SpringBoot集成SpringSecurity和JWT做登陸鑒權(quán)的實現(xiàn)的文章就介紹到這了,更多相關(guān)SpringBoot JWT 登陸鑒權(quán)內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 控显科技 - 工控一体机、工业显示器、工业平板电脑源头厂家 | 叉车电池-叉车电瓶-叉车蓄电池-铅酸蓄电池-电动叉车蓄电池生产厂家 | 通用磨耗试验机-QUV耐候试验机|久宏实业百科 | 大数据营销公司_舆情监测软件_上海SEO公司-文军营销官网 | 热处理温控箱,热处理控制箱厂家-吴江市兴达电热设备厂 | 校园气象站_超声波气象站_农业气象站_雨量监测站_风途科技 | 过跨车_过跨电瓶车_过跨转运车_横移电动平车_厂区转运车_无轨转运车 | 药品/药物稳定性试验考察箱-埃里森仪器设备(上海)有限公司 | 证券新闻,热播美式保罗1984第二部_腾讯1080p-仁爱影院 | 济南侦探调查-济南调查取证-山东私家侦探-山东白豹调查咨询公司 密集架|电动密集架|移动密集架|黑龙江档案密集架-大量现货厂家销售 | 扬尘在线监测系统_工地噪声扬尘检测仪_扬尘监测系统_贝塔射线扬尘监测设备「风途物联网科技」 | 广东青藤环境科技有限公司-水质检测| 葡萄酒灌装机-食用油灌装机-液体肥灌装设备厂家_青州惠联灌装机械 | 品牌广告服务平台,好排名,好流量,好生意。 | 微妙网,专业的动画师、特效师、CG模型设计师网站! - wmiao.com 超声波电磁流量计-液位计-孔板流量计-料位计-江苏信仪自动化仪表有限公司 | NBA直播_NBA直播免费观看直播在线_NBA直播免费高清无插件在线观看-24直播网 | 双舌接地线-PC68数字式高阻计-ZC36|苏海百科 | PC阳光板-PC耐力板-阳光板雨棚-耐力板雨棚,厂家定制[优尼科板材] | 游戏版号转让_游戏资质出售_游戏公司转让-【八九买卖网】 | 缠膜机|缠绕包装机|无纺布包装机-济南达伦特机械设备有限公司 | 亿立分板机_曲线_锯片式_走刀_在线式全自动_铣刀_在线V槽分板机-杭州亿协智能装备有限公司 | 有福网(yofus.com)洗照片冲印,毕业聚会纪念册相册制作个性DIY平台 | 包装盒厂家_纸盒印刷_礼品盒定制-济南恒印包装有限公司 | 锂电混合机-新能源混合机-正极材料混料机-高镍,三元材料混料机-负极,包覆混合机-贝尔专业混合混料搅拌机械系统设备厂家 | Eiafans.com_环评爱好者 环评网|环评论坛|环评报告公示网|竣工环保验收公示网|环保验收报告公示网|环保自主验收公示|环评公示网|环保公示网|注册环评工程师|环境影响评价|环评师|规划环评|环评报告|环评考试网|环评论坛 - Powered by Discuz! | 萃取箱-萃取槽-PVC萃取箱厂家-混合澄清槽- 杭州南方化工设备 | 淬火设备-钎焊机-熔炼炉-中频炉-锻造炉-感应加热电源-退火机-热处理设备-优造节能 | 佛山商标注册_商标注册代理|专利注册申请_商标注册公司_鸿邦知识产权 | 煤矿支护网片_矿用勾花菱形网_缝管式_管缝式锚杆-邯郸市永年区志涛工矿配件有限公司 | 大流量卧式砂磨机_强力分散机_双行星双动力混合机_同心双轴搅拌机-莱州市龙跃化工机械有限公司 | 常州减速机_减速机厂家_常州市减速机厂有限公司 | 德州网站制作 - 网站建设设计 - seo排名优化 -「两山建站」 | 网站建设_网站制作_SEO优化推广_百度推广开户_朋友圈网络科技 | 英超直播_英超免费在线高清直播_英超视频在线观看无插件-24直播网 | 开业庆典_舞龙舞狮_乔迁奠基仪式_开工仪式-神挚龙狮鼓乐文化传媒 | 丹佛斯变频器-Danfoss战略代理经销商-上海津信变频器有限公司 | 塑料撕碎机_编织袋撕碎机_废纸撕碎机_生活垃圾撕碎机_废铁破碎机_河南鑫世昌机械制造有限公司 | 安全阀_弹簧式安全阀_美标安全阀_工业冷冻安全阀厂家-中国·阿司米阀门有限公司 | 搪玻璃冷凝器_厂家-越宏化工设备 | 知网论文检测系统入口_论文查重免费查重_中国知网论文查询_学术不端检测系统 | 污水提升器,污水提升泵,地下室排水,增压泵,雨水泵,智能供排水控制器-上海智流泵业有限公司 |