This article is a sub-chapter of the personal development framework for SpringBoot2.1. Please read the personal development framework for SpringBoot2.1

Back-end project address: personal application development framework for SpringBoot2.1

Front-end project address: yWH-vuE-admin

Thank you PanJiaChen for creating such a good vue backend management template. PanJiaChen has a series of tutorials, the address of the series of articles in the preview address, and the address of the project Github, feel that PanJiaChen is cool.

  • Vue-element-admin Preview address: vue-element-admin

  • Vue official document: VUE official document

  • Node installation: Node.js installation and environment variable configuration

Download the Vue-admin-template from PanJiaChen. This is the vue-element-admin template recommended by PanJiaChen for secondary development. Vue-element-admin is intended to be an integrated solution that allows us to get what we need when we need it.

I’m not going to describe the structure of the project here, it’s all covered in the Series of articles on Daishen. After we download the project, we try to run it locally and make sure there are no errors before we go to the next step.

Start the initial vue-admin-template project

Run CMD in the downloaded project and download the dependencies required by the project before starting

NPM install... npm run devCopy the code

Renderings, now login or the default user, we want to achieve the function is: front-end and back-end interaction, and query the user in the database when whether there is permission to login.

After running, the login interface looks like the picture above, with less functions than the preview, so we will add them one by one according to our own needs.

The back-end project

We want the back end to interact with the front end, in fact, we still need to modify a lot of places, here is the first introduction to modify the back end, in the project where the front end is separated, most of the requests are authenticated by Token, I also implemented JWT and SpringSecurity to protect THE API, they are not directly related in my understanding. It is a cooperative relationship. SpringSecurity decides what requests can access our server. JWT determines whether the requests that can be accessed carry tokens or not, and the ones that are not carried are rejected.

Use JWT and Spring Security to protect the REST API

The creation of JWT

Add the following content to the application-security.yml file in the security module: JWT encryption string is a pre-written, which is equivalent to three constants, nothing special, will be loaded in the class later, if you don’t have the time, you can directly define constants in the class.

jwt:
  header: token   # JWT request header
  secret: eyJleHAiOjE1NDMyMDUyODUsInN1YiI6ImFkbWluIiwiY3Jl   # JWT encrypted string
  expiration: 3600000   # JWT Token valid time (ms) one hour
Copy the code

Create the JwtTokenUtil utility class in the utils package of the YWH-starter-Security module. If you want to see the detailed code, you can go to my GitHub to see the detailed code.

package com.ywh.security.utils;

/** * CreateTime: 2019-01-22 10:27 * ClassName: JwtTokenUtil * Package: com.ywh.security.utils * Describe: * JWT tool class **@author YWH
 */
@Data
@Component
@ConfigurationProperties(prefix = "jwt")
public class JwtTokenUtil {

    private String secret;

    private Long expiration;

    private String header;


    /** * Generate token ** from data declaration@paramClaims Data declaration *@returnTokens * /
    private String generateToken(Map<String, Object> claims) {
        Date expirationDate = new Date(System.currentTimeMillis() + expiration);
        returnJwts.builder() .setClaims(claims) .setExpiration(expirationDate) .signWith(SignatureAlgorithm.HS256, secret) .compact();  }/** * Generate token *@returnTokens * /
    public String generateToken(String userName) {
        Map<String, Object> claims = new HashMap<>(2);
        claims.put("sub", userName);
        claims.put("created".new Date());
        returngenerateToken(claims); }... Code omitted in the middle}Copy the code

Back in our front end request, we are going to every judge whether carry a token, we hand over to the interceptor to perform this task, create JwtAuthenticationTokenFilter interceptors

package com.ywh.security.filter;

/** * CreateTime: 2019-01-29 18:15 * ClassName: JwtAuthenticationTokenFilter * Package: Com.ywh.security.filter * Describe: * Spring interceptor * *@author YWH
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    private final static Logger log = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);

    private JwtTokenUtil jwtTokenUtil;
    private UserDetailsService userDetailsService;

    @Autowired
    public JwtAuthenticationTokenFilter(JwtTokenUtil jwtTokenUtil, UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
        this.jwtTokenUtil = jwtTokenUtil;
    }

    /** * The main function of the interceptor is to intercept the request and determine whether the request carries a token. If the request does not carry a token, the request is rejected. *@paramHttpServletRequest HTTP request *@paramHttpServletResponse HTTP response *@paramFilterChain interceptor *@throwsServletException Exception information *@throwsIOException Exception information */
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {

        // Obtain the JWT token from the request
        String authHeader = httpServletRequest.getHeader(jwtTokenUtil.getHeader());
        // Verify that the token exists
        if(StringUtils.isNotEmpty(authHeader)){
            // Obtain the user name based on the token
            String userName = jwtTokenUtil.getUsernameFromToken(authHeader);
            if(userName ! =null && SecurityContextHolder.getContext().getAuthentication() == null) {// Obtain user information by user name
                UserDetails userDetails = userDetailsService.loadUserByUsername(userName);
                // Verify that the token and user information match
                if(jwtTokenUtil.validateToken(authHeader,userDetails)){
                    / / then construct UsernamePasswordAuthenticationToken object
                    // Finally bind to the current request to retrieve user information in subsequent requests
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(newWebAuthenticationDetailsSource().buildDetails(httpServletRequest)); SecurityContextHolder.getContext().setAuthentication(authentication); } } } filterChain.doFilter(httpServletRequest, httpServletResponse); }}Copy the code

Once the interceptor is in place, we need to modify the configure(HttpSecurity HttpSecurity) method in the SecurityConfigurer class, which I introduced in the last two articles. In the following code, I write the back-end implementation of the cross-domain request. We won’t have to implement cross-domain requests on the front end, but I’ll write how the front end implements cross-domain requests.

	/** * Configure how to protect our requests through interceptors, what is allowed and what is not, and allow configuration for specific HTTP requests based on security considerations *@param httpSecurity http
     * @throwsThe Exception Exception * /
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {

        httpSecurity
                // Temporarily ban CSRC otherwise cannot be submitted
                .csrf().disable()
                / / the session management
                .sessionManagement()
                . / / we use SessionCreationPolicy STATELESS STATELESS Session mechanism (that is, the Spring does not use the HTTPSession), for all request permission to check,
                // Spring Security's interceptor will determine if all requests have "X-Auth-token" on their headers.
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                If the second user logs in, the first user is kicked out and redirected to the login page
                .maximumSessions(1).expiredUrl("/login.html");
        httpSecurity
                // Start authentication
                .authorizeRequests()
                // Allow static files and login pages
                .antMatchers("/static/**").permitAll()
                .antMatchers("/auth/**").permitAll()
                .antMatchers("/login.html").permitAll()
                // Other requests require authentication login
                .anyRequest().authenticated();

        / / injection we just write good JWT filters, add before UsernamePasswordAuthenticationFilter filters
        httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        // This block configures cross-domain requests
         ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
        // Let Spring Security pass all preFlight requests
        registry.requestMatchers(CorsUtils::isPreFlightRequest).permitAll();
 }
 
 /** * this is the * that configures cross-domain requests@returnCors filter */
    @Bean
    public CorsFilter corsFilter(a) {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/ * *", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
Copy the code

As you can see from the code above, I added our interceptor to SpringSecurity’s interceptor chain, which enabled them to cooperate with each other. The next step was to create our service and Controller, which implemented the basic login and logout, and returned a Token when the user logged in. The front end is stored in a local cache (localStorage) or sessionStorage for future requests.

package com.ywh.security.service.impl;

/** * CreateTime: 2019-01-25 * ClassName: SysUserServiceImpl * Package: com.ywh.security.service.impl * Describe: * Implementation class of the business logic interface *@author YWH
 */
@Service
public class SysUserServiceImpl extends BaseServiceImpl<SysUserDao.SysUserEntity> implements SysUserService {

    private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class);

    @Autowired
    private SysUserDao dao;

    @Autowired
    private AuthenticationManager authenticate;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    /** * Get user details *@paramUsername username *@returnEntity class * /
    @Override
    public SysUserEntity findUserInfo(String username) {
        return dao.selectByUserName(username);
    }

    /** * user login *@paramUsername username *@param"Password," password *@returnToken */ is returned after successful login
    @Override
    public String login(String username, String password) throws AuthenticationException {
        // Internal login request
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
        // Verify that you have permissions
        Authentication auth = authenticate.authenticate(authRequest);
        log.debug("= = = = = = = = = = = = = = = permissions = = = = = = = = = = = =" + auth);
        SecurityContextHolder.getContext().setAuthentication(auth);

        returnjwtTokenUtil.generateToken(username); }}Copy the code

The implementation of Controller, because they are the simplest implementation, can be modified according to their own needs, or later according to their own ideas to add corresponding implementation.

package com.ywh.security.controller;

/** * CreateTime: 2019-01-28 16:06 * ClassName: AuthController * Package: com.ywh.security.controller * Describe: * Permission controller * *@author YWH
 */
@RestController
@RequestMapping("auth")
public class AuthController {


    private static final Logger LOG = LoggerFactory.getLogger(AuthController.class);


    @Autowired
    private SysUserService sysUserService;


    /** * login *@paramMap receiver *@returnReturns the token * /
    @PostMapping("login")
    public Result login(@RequestBody Map<String, String> map){
        try {
            String token = sysUserService.login(map.get("username"), map.get("password"));
            return Result.successJson(token);
        }catch (AuthenticationException ex){
            LOG.error("Login failed",ex);
            returnResult.errorJson(BaseEnum.PASSWORD_ERROR.getMsg(),BaseEnum.PASSWORD_ERROR.getIndex()); }}/** * User details *@returnUser details */
    @Cacheable(value = "userInfo")
    @GetMapping("userInfo")
    public Result userInfo(a){
        Object authentication = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if(authentication instanceof SecurityUserDetails){
            return Result.successJson(sysUserService.findUserInfo(((SecurityUserDetails) authentication).getUsername()));
        }
        return Result.errorJson(BaseEnum.LOGIN_AGIN.getMsg(),BaseEnum.LOGIN_AGIN.getIndex());
    }

    @PostMapping("logOut")
    public Result logOut(a){
        return Result.successJson("Exit successful, because the token itself is stateless, if you control the lifetime of the token through redis, it becomes stateful, so there is no good solution for now."); }}Copy the code

This is the end of the back-end project modification, I think the most important thing is to understand what they are how to work, know the process, we can understand a lot, step by step to write down, encounter not more Google more Baidu.

Process summary for JWT and SpringSecurity:

  • Start with a utility class that generates tokens.
  • Implement an interceptor for JWT that determines whether each request carries a Token.
  • Modify the configuration class for Security so that JWT and Security have a cooperative relationship.
  • Create a service for the simplest login and user query operations.
  • Create a Controller that provides the interface required by the front end.

The front-end project

After running the vue-elment-template front-end project above, we need to modify a lot of places, which is quite miscellaneous, I also encountered a mistake to modify a place.

I have implemented a back-end cross-domain approach in the back-end project, but the front-end can also implement cross-domain requests, either way.

  • SRC /utils/request.js baseURL address should be deleted, and proxyTable in /config/index.js should be used to solve the cross-domain problem.
proxyTable: {
  '/core': {
    target: 'http://192.168.0.117:8082'.// Interface domain name
    // Secure: false, // This parameter needs to be configured for HTTPS interfaces
    changeOrigin: true.// This parameter is required if the interface is cross-domain
    pathRewrite: {
      '^/core': '/core'}}},Copy the code
  • Config \dev.env.js and config\prod.env.js modify the access root path
'use strict'
module.exports = {
  NODE_ENV: '"production"'.BASE_API: '"http://localhost:8082/core/"',}Copy the code
  • SRC \ API \login.js solve the access path problem, because our controller path is auth/**, so we need to change to our own path, I only write an example, the rest of the change.
export function login(username, password) {
  return request({
    url: '/auth/login'.method: 'post'.data: {
      username,
      password
    }
  })
}
Copy the code
  • SRC \utils\request.js modifys the token name in the Header. This is determined by the back end.

  • SRC \store\modules\user.js modify login and other problems, here we need to modify a little more, language description is not very good description, I briefly robbed two pictures, if you encounter errors to solve it just more familiar with it.

The above are almost the big problems I encountered in the front end, there are a lot of small problems, I will not post one, after setting the above, you can first try to run, if not, you can compare my GitHub code.

rendering

When we log in again, we can see that we have queried the user information in the database and logged in.

If you log in as admin, the user name or password is incorrect