Series directory

preface

After writing the last article, I studied it for a long time. In the end, I found that JWT seemed inappropriate for our project. Unlike Vue, Layui can not set tokens globally through Axios (maybe I don’t know how to set tokens globally because I like layui). Here is a brief introduction to the front-end operation (vUE as an example), mainly is to get the token after it is stored in localstorage or cookies, and then get the token from localstorage or cookies to set the global request header, it is ok. However, there is no problem with the content of JWT in the previous article, and normal use is also the same step.

The specific content

Remove the JWT

So since you’re not going to use JWT anymore, go back to using cookies and sessions. So we need to get our program back to where it was at the end of Field five.

Here is not very good explains, is to delete related to JWT, need to modify the place has MyAuthenticationSuccessHandler, JwtAuthenticationTokenFilter, SpringSecurityConfig three places, Delete the JWT content. (Not much space)

Front-end Prompt

I’ve modified the login page to give you a message if the login fails

<! DOCTYPEhtml>
<html  xmlns:th="http://www.thymeleaf.org">
	<head>
		<meta charset="utf-8">
		<title></title>
		<link rel="stylesheet" href="/PearAdmin/admin/css/pearForm.css" />
		<link rel="stylesheet" href="/PearAdmin/component/layui/css/layui.css" />
		<link rel="stylesheet" href="/PearAdmin/admin/css/pearButton.css" />
		<link rel="stylesheet" href="/PearAdmin/assets/login.css" />
	</head>
	<body background="PearAdmin/admin/images/background.svg" >
	    <form class="layui-form" method="get">
			<div class="layui-form-item">
				<img class="logo" src="PearAdmin/admin/images/logo.png" />
				<div class="title">M-S-P Admin</div>
				<div class="desc">Spring Security's entitlement management system is a real war</div>
			</div>
            <div class="layui-form-item">
				<input id="username" name="username" placeholder=Account name: type="text" hover class="layui-input" required lay-verify="username"/>
			</div>
			<div class="layui-form-item">
				<input id="password" name="password" placeholder="Key code:" type="password"  hover class="layui-input" required lay-verify="password"/>
			</div>
            <div class="layui-form-item">
                <input id="captcha" name="captcha" placeholder="Verification Code :" type="text"  hover class="layui-verify" style="border: 1px solid #dcdfe6;" required lay-verify="captcha">
                <img id="captchaImg" src="/captcha" width="130px" height="44px" onclick="this.src=this.src+'? '+Math.random()" title="Hit Refresh"/>
            </div>

			<div class="layui-form-item">
				<input type="checkbox" id="rememberme" name="rememberme" title="Remember the password" lay-skin="primary" checked>
			</div>
            <div class="layui-form-item">
				<button style="background-color: #5FB878! important;" class="pear-btn pear-btn-primary login" lay-submit lay-filter="formLogin">Insert into</button>
			</div>
		</form>
		<script src="/PearAdmin/component/layui/layui.js" charset="utf-8"></script>
		<script>
			layui.use(['form'.'element'.'jquery'].function() {
				var form = layui.form;
				var element = layui.element;
				var $ = layui.jquery;
				// $("body").on("click",".login",function(obj){
				// location.href="/api/admin"
				// })
				form.verify({
					username: function(value) {
						if (value.length <= 0 ) {
							return 'User name cannot be empty'; }},password: function (value) {
						if (value.length <= 0) {
							return 'Password cannot be empty'; }},captcha: function (value) {
						if (value.length <= 0) {
							return 'Captcha cannot be null';
						}
						if(value.length ! = =4) {
							return 'Please enter a captcha in the correct format';
						}
					}
				})
				form.on('submit(formLogin)'.function() {
					$.ajax({
						url:'/login'.type:'post'.dataType:'text'.data: {username: $('#username').val(),
							password: $('#password').val(),
							captcha: $('#captcha').val(),
							rememberme: $('#rememberme').val()
						},
						success:function(result){
							var restjson = JSON.parse(result)
							if (restjson.success) {
								// layui.data("token", {
								// key: "Authorization",
								// value: "Bearer "+ restjson.jwt
								// });
								layer.msg(restjson.msg,{icon:1.time:1000},function () {
									location.href = "/";

								});
							}else {
								layer.msg(restjson.msg,{icon:2.time:1000},function () {$("#captchaImg").attr("src"."/captcha" + "?" + Math.random());
								});
								return false; }}})return false;
				});
			})
		</script>
	</body>
</html>
Copy the code

The back end also made a failed login handler

/ * * *@author codermy
 * @createTime2020/8/2 * /
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.setCharacterEncoding("utf-8");// Change the encoding format
        httpServletResponse.setContentType("application/json");
        httpServletResponse.getWriter().write(JSON.toJSONString(Result.error().message(e.getMessage())));// Return information}}Copy the code

AuthenticationFailureHandler exception class is an abstract, his common subclass

UsernameNotFoundException users find BadCredentialsException bad credentials AccountStatusException abnormal state it contains the following subclasses AccountExpiredException account overdue LockedException account lock DisabledException account unavailable CredentialsExpiredException certificate has expiredCopy the code

All are exceptions that the user may encounter when logging in

Modified SpringSecurityConfig complete

/ * * *@author codermy
 * @createTime2020/7/15 * /
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private VerifyCodeFilter verifyCodeFilter;// Captcha interceptor
    @Autowired
    MyAuthenticationSuccessHandler authenticationSuccessHandler;// Logon success logic
    @Autowired
    private MyAuthenticationFailureHandler authenticationFailureHandler;Logon failure logic
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;/ / JWT interceptors
    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;// No permission interceptor
    @Autowired
    private RestfulAccessDeniedHandler accessDeniedHandler;// Have no access to jSON-formatted data



    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .antMatchers(HttpMethod.GET,
                        "/swagger-resources/**"."/PearAdmin/**"."/**/*.html"."/**/*.css"."/**/*.js"."/swagger-ui.html"."/webjars/**"."/v2/**");// Permits static resources
    }

    / * * * anyRequest | match all the access request path * | SpringEl expression results when they can access to true * anonymous | anonymous access to * denyAll | users cannot access * FullyAuthenticated | user authentication can access fully (not remember - automatic login under me) * hasAnyAuthority | if you have parameters, parametric representation rights, have access any one can access * hasAnyRole | if there is a parameter, Parameters according to role, then any one role can access * hasAuthority | if you have parameters, parameter access, is it can access * hasIpAddress | if you have parameters, parameter indicates the IP address, if the user IP and matching parameters, Can access * hasRole | if you have parameters, parameter, Its role can access * permitAll | users can access any * rememberMe | allow authenticated by remember - me login user access * | accessible * / after the user login
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
        http.csrf().disable()/ / close CSRF
                //.sessionManagement(
                // .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                // .and()
                .httpBasic().authenticationEntryPoint(restAuthenticationEntryPoint)// Return data in JSON format to the front-end when not logged in
                .and()
                .authorizeRequests()
                .antMatchers("/captcha").permitAll()// Anyone can access this request
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")// Access to the login page is not restricted
                .loginProcessingUrl("/login")// Intercepting the request
                .successHandler(authenticationSuccessHandler) // Login succeeded
                .failureHandler(authenticationFailureHandler) // Login failed
                .permitAll()
                .and()
                .rememberMe().rememberMeParameter("rememberme")
                // Prevent iframe from crossing domains
                .and()
                .headers()
                .frameOptions()
                .disable()
                .and();

        // Disable caching
        http.headers().cacheControl();

        // Add JWT interceptor
        // http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler); // No access to data returned in JSON format
    }

    @Bean
    public PasswordEncoder passwordEncoder(a) {
        return new BCryptPasswordEncoder(12);
    }

    /** * Authentication interface */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); }}Copy the code

Problems encountered

A, Springsecurity UsernameNotFoundException anomaly can not be normal in the capture

See this article for detailed explanations (very detailed, including solutions)

In short, I throw UsernameNotFoundException but in the end will be converted to BadCredentialsException anomaly. I won’t go into more details here, the above article is very detailed.

Please refer to the article for solutions. What I use is opportunistic method, that is, direct selling BadCredentialsException rather than UsernameNotFoundException anomaly. After all, the final message is a vague “username or password error,” rather than a specific error.

2. Exceptions thrown by filter cannot be uniformly processed

This problem is mainly related to the interception of the verification code, the front end can not get the verification code error message. Instead of using an interceptor to handle the captcha, we can customize a login request to get around this problem.

This problem is also the original writing problem, in fact, the original need to throw the exception, directly output to the page prompt.

I was looking for ways to do this and I found two ways to do this

  • 1. The Spring Boot project throws a handler in the custom Filter that the exception cannot catch
  • 2. The global unified exception processing fails to intercept the CATCH exception in FILTER

After Syria

This article is a bit messy, the blogger’s writing is really not very good, so it may be a bit hard to understand when describing some of the issues. If you have any questions during the learning process, please add me to QQ (on my code cloud homepage) and let’s discuss and learn together. In the next article we implement user operation logging and exception logging

ingiteeandgithubThe source code is available for updating with this article series