本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog2,之前的微信号好友位已满,备注:返现
受密码保护的文章请关注“业余草”公众号,回复关键字“0”获得密码
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
腾讯云】1核2G5M轻量应用服务器50元首年,高性价比,助您轻松上云
一个网站的登录页面就相当于一个人的脸,脸往往给人留下第一印象。印象好,就会带来好体验。好体验就可能有更高的活跃度,所以像我前面《SpringBoot + spring-security-oauth2 实现仿微信,QQ,微博等授权认证》中的漂亮的登录页面该如何定制呢?本文,我们一起来实践一把吧。
严格来说,这个登录页面的定制化,并不算 spring-security-oauth2 中的功能,而是靠 Spring Security 来实现的。在 OAuth2 中我们可以针对不同的应用有不同的登录页面,这个登录页面的代码在 Server 端,认证我们可以放在客户端 Client 中。
实现的原理很简单,就是当我们要访问未授权的页面时,进行拦截,通过 @EnableOAuth2Sso 注解,让它通过 OAuth2 到 AuthenticationProvider 中去认证。
首先,我们要实现 WebSecurityConfigurerAdapter,配置哪些资源需要拦截,哪些不需要认证。
@Configuration @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) @EnableWebSecurity static class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authenticationProvider); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); http.antMatcher("/oauth/**") .authorizeRequests() .antMatchers("/oauth/index").permitAll() .antMatchers("/oauth/token").permitAll() .antMatchers("/oauth/check_token").permitAll() .antMatchers("/oauth/confirm_access").permitAll() .antMatchers("/oauth/error").permitAll() .antMatchers("/oauth/approvale/confirm").permitAll() .antMatchers("/oauth/approvale/error").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/oauth/index") .loginProcessingUrl("/oauth/login"); } @Autowired private CustomAuthenticationProvider authenticationProvider; }
登录认证部分,我们通过实现 AuthenticationProvider 接口,对接数据库来实现。
@Component public class CustomAuthenticationProvider implements AuthenticationProvider { @Autowired private AccountService accountService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String name = authentication.getName(); String password = authentication.getCredentials().toString(); Account account = accountService.authUser(name, password); if (account == null) { throw new AuthenticationCredentialsNotFoundException("Account is not found."); } List<GrantedAuthority> grantedAuths = AuthorityUtils.createAuthorityList(account.getRoleString()); return new UsernamePasswordAuthenticationToken(name, password, grantedAuths); } @Override public boolean supports(Class<?> authentication) { return authentication.equals(UsernamePasswordAuthenticationToken.class); } }
业务逻辑层 AccountService 的代码如下:
@Service public class AccountService { @Autowired private AccountRepository accountRepository; public Account authUser(String userName, String password) { Account u = accountRepository.findByUserName(userName); if (u == null) { return null; } if (!u.getPassword().equals(password)) { return null; } return u; } }
AccountRepository 代码如下:
public interface AccountRepository { @Select("select id, user_name as userName, email, password, role_string as roleString from account where user_name=#{user_name}") Account findByUserName(String userName); }
实体类:
@SuppressWarnings("serial") public class Account implements Serializable { private Integer id; private String userName; private String email; private String password; private String roleString; // ...setter/getter }
SQL:
CREATE TABLE account ( id serial NOT NULL, user_name character varying(50), email character varying(255), password character varying(512), role_string character varying(50), CONSTRAINT account_pkey PRIMARY KEY (id) ); INSERT INTO account(user_name, email, password, role_string) VALUES ('user', 'user@sample.com', '123', 'ROLE_USER');
扫描包:
@Configuration @MapperScan("com.rensanning") @EnableTransactionManagement(proxyTargetClass = true) static class RepositoryConfig { }
登录页面代码:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> <script src="https://cdn.bootcss.com/vue/2.5.17/vue.min.js"></script> <script src="https://unpkg.com/element-ui/lib/index.js"></script> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> </head> <body> <div class="login-box" id="app" > <el-form action="/auth/login" method="post" label-position="left" label-width="0px" class="demo-ruleForm login-container"> <h2 class="title" >统一认证登录平台</h2> <el-form-item> <el-input type="text" name="username" v-model="username" auto-complete="off" placeholder="账号"></el-input> </el-form-item> <el-form-item> <el-input type="password" name="password" v-model="password" auto-complete="off" placeholder="密码"></el-input> </el-form-item> <el-form-item style="width:100%; text-align:center;"> <el-button type="primary" style="width:47%;" @click.native.prevent="reset">重 置</el-button> <el-button type="primary" style="width:47%;" native-type="submit" :loading="loading">登 录</el-button> </el-form-item> <el-form> </div> </body> <script type="text/javascript"> new Vue({ el : '#app', data : { loading: false, username: 'admin', password: '123' }, methods : { reset: function() { this.username = 'admin' this.password = '123' } } }) </script> <style lang="scss" scoped> .login-container { -webkit-border-radius: 5px; border-radius: 5px; -moz-border-radius: 5px; background-clip: padding-box; margin: 100px auto; width: 320px; padding: 35px 35px 15px 35px; background: #fff; border: 1px solid #eaeaea; box-shadow: 0 0 25px #cac6c6; } .title { margin: 0px auto 20px auto; text-align: center; color: #505458; } </style> </html>
注意,pom.xml 中,需要引入 spring-boot-starter-thymeleaf,最终运行效果:
参考资料
- https://stackoverflow.com/questions/29618658/spring-how-to-create-a-custom-access-and-refresh-oauth2-token
- https://stackoverflow.com/questions/29345508/spring-oauth2-custom-oauth-approval-page-at-oauth-authorize
最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号:xttblog2。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!
本文原文出处:业余草: » spring-security-oauth2 自定义登录页面